/* slab.css is the stylesheet samverkr serves and the Slab scheme in full: the
   tokens, then the parts every page is built from. Nothing else declares a
   colour, a border or a corner in samverkr's HTML — a page asks for a part by
   name and gets what this file says.

   Slab is a greyscale workstation of machined grey, chamfered bevels and a
   dithered desktop, with colour reserved for what went wrong and what went
   right. There is no light mode and no dark mode to keep in step.

   docs/design/slab.html is the specimen sheet for this file and links it directly,
   so the sheet cannot show a part the app does not serve. docs/design/README.md
   names the tests that hold the sheet, the pages and this file to one scheme. */

/* Self-hosted rather than fetched from a font service: the content security
   policy admits nothing outside the site, and a self-hosted factory runs
   without the internet. The urls are relative to this file, which is also how
   they resolve when the specimen sheet links it from docs/design/. Served, the same
   urls reach /static/fonts by way of WriteFont. */
@font-face {
	font-display: swap;
	font-family: "IBM Plex Mono";
	font-style: normal;
	font-weight: 400;
	src: url(fonts/ibm-plex-mono-400.woff2) format("woff2");
}

@font-face {
	font-display: swap;
	font-family: "IBM Plex Mono";
	font-style: normal;
	font-weight: 700;
	src: url(fonts/ibm-plex-mono-700.woff2) format("woff2");
}

:root {
	color-scheme: light;

	--color-paper: #8f8f8f;
	--color-panel: #c8c8c8;
	--color-strip: #b9b9b9;
	--color-field: #ececec;
	--color-ink: #141414;
	--color-muted: #4a4a4a;
	--color-line: #0d0d0d;
	--color-accent: #1c1c1c;
	--color-accent-hi: #333333;
	--color-accent-ink: #f0f0f0;
	--color-danger: #8d1d13;
	--color-danger-field: #e6d8d5;
	/* The success pair is the danger pair turned green at the same luminance,
	   so a note box weighs what an error box weighs. */
	--color-success: #0c542a;
	--color-success-field: #cbe0d3;
	--color-title: #1c1c1c;
	--color-title-ink: #f0f0f0;
	--color-bevel-hi: #f4f4f4;
	--color-bevel-lo: #5a5a5a;

	/* The four edges a piece of machined grey can show: raised, pressed, sunk
	   below the surface, and lit along the top. */
	--shadow-bevel: inset 1px 1px 0 var(--color-bevel-hi), inset -1px -1px 0 var(--color-bevel-lo);
	--shadow-bevel-in: inset -1px -1px 0 var(--color-bevel-hi), inset 1px 1px 0 var(--color-bevel-lo);
	--shadow-sunken: inset 1px 1px 0 var(--color-bevel-lo), inset -1px -1px 0 var(--color-bevel-hi);
	--shadow-strip: inset 0 1px 0 var(--color-bevel-hi);
	--shadow-frame: inset 1px 1px 0 var(--color-bevel-hi), inset -1px -1px 0 var(--color-bevel-lo),
		4px 4px 0 rgba(0, 0, 0, 0.35);

	--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
	--fs-base: 0.8125rem;
	--fs-meta: 0.75rem;
	--leading: 1.55;
	/* The tracking every uppercased label carries, so a field label and a panel
	   title are the same piece of type. */
	--track-label: 0.08em;
}

/* The reset a browser leaves behind: no rounded corners anywhere, one
   typeface, and form controls that take the type they are given rather than
   the one the UA picked. Margins are zeroed where a part needs it rather than
   everywhere, the way the parts below read. */
*,
*::before,
*::after {
	box-sizing: border-box;
	border-radius: 0;
}

html {
	-webkit-text-size-adjust: 100%;
}

/* The screen is a column: a title bar along the top, the desk, and a status
   bar along the bottom. The desk takes the height the bars leave, so the
   status bar sits on the bottom edge of a short page and below the content of
   a long one. */
body {
	background: var(--color-paper);
	color: var(--color-ink);
	display: flex;
	flex-direction: column;
	font-family: var(--font-mono);
	font-size: var(--fs-base);
	font-weight: 400;
	letter-spacing: 0;
	line-height: var(--leading);
	margin: 0;
	min-height: 100vh;
}

input,
button,
select,
textarea {
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
}

button {
	appearance: none;
	background: transparent;
}

p {
	margin: 0;
}

code,
kbd {
	font-family: inherit;
	font-size: 0.95em;
}

/* The desktop under the window: two greys in a 2px checkerboard, which is how
   a machine with four bits of colour made a third grey. */
.desk {
	background-color: var(--color-paper);
	background-image: repeating-conic-gradient(from 0deg, #888888 0% 25%, #979797 0% 50%);
	background-size: 2px 2px;
	flex: 1;
	padding: 24px;
}

/* The window. */

.chrome {
	background: var(--color-panel);
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-frame);
	margin: 0 auto;
	max-width: 42rem;
	padding: 0 0 0 1px;
}

/* Centred by flex rather than by a third grid column, so a title with no
   window buttons beside it still sits in the middle of the bar. */
.titlebar {
	align-items: center;
	background: var(--color-title);
	display: flex;
	gap: 12px;
	padding: 5px 6px;
}

.titlebar-text {
	color: var(--color-title-ink);
	flex: 1;
	font-size: inherit;
	font-weight: 700;
	letter-spacing: 0.06em;
	margin: 0;
	text-align: center;
	text-shadow: 1px 1px 0 #000;
	text-transform: uppercase;
}

/* The screen's title bar names the system and reads from the left, so a
   window's centred title is never mistaken for it. */
body > .titlebar .titlebar-text {
	text-align: start;
}

.titlebar-actions {
	align-items: center;
	display: flex;
	gap: 3px;
	margin: 0;
	margin-inline-start: auto;
}

.titlebar-user {
	color: var(--color-title-ink);
	padding-inline-end: 6px;
}

/* A command on the title bar is a word that inverts under the pointer, the
   way a menu title does, so the bar keeps the height of its text. A .tb-btn
   is sized for one glyph and squashes a word. */
.titlebar-item {
	border: 0;
	color: var(--color-title-ink);
	cursor: pointer;
	padding: 0 8px;
}

.titlebar-item:hover {
	background: var(--color-panel);
	color: var(--color-ink);
}

/* The ink ring every other control takes would vanish on the title bar. */
.titlebar-item:focus-visible {
	outline-color: var(--color-title-ink);
}

.tb-btn {
	background: var(--color-panel);
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-bevel);
	cursor: pointer;
	line-height: 1;
	padding: 2px 7px;
}

.tb-btn:active {
	box-shadow: var(--shadow-bevel-in);
}

.menubar {
	align-items: center;
	background: var(--color-panel);
	border-bottom: 1px solid var(--color-line);
	box-shadow: var(--shadow-strip);
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	padding: 6px 8px;
}

.menubar-item {
	border: 1px solid transparent;
	color: var(--color-ink);
	padding: 3px 9px;
	text-decoration: none;
}

.menubar-item:hover {
	background: var(--color-field);
	border-color: var(--color-line);
}

.menubar-item.is-active {
	background: var(--color-accent);
	color: var(--color-accent-ink);
}

/* The name of the installation, which is the only thing in the menu bar that
   is not a destination. Compose it onto .menubar-item. */
.brand {
	font-weight: 700;
	letter-spacing: var(--track-label);
	text-transform: uppercase;
}

.menubar-user {
	color: var(--color-muted);
	margin-inline-start: auto;
	padding: 3px 9px;
}

.content {
	background: var(--color-panel);
	box-shadow: var(--shadow-sunken);
	padding: 16px;
}

.statusbar {
	align-items: center;
	background: var(--color-panel);
	border-top: 1px solid var(--color-line);
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	padding: 5px 8px;
}

/* The bevel of a status cell is inverted: sunk into the bar rather than raised
   out of it, which is what the reversed pair of edges reads as. */
.sb-cell {
	border: 1px solid var(--color-line);
	border-color: var(--color-bevel-lo) var(--color-bevel-hi) var(--color-bevel-hi) var(--color-bevel-lo);
	color: var(--color-muted);
	font-size: var(--fs-meta);
	padding: 1px 8px;
}

/* Layout. Panels are the only boxes, and they stack in one or two columns
   depending on how much room they have. */

.grid {
	align-items: start;
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
}

.panel {
	background: var(--color-panel);
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-bevel);
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.panel-full {
	grid-column: 1 / -1;
}

.panel-title {
	background: var(--color-strip);
	border-bottom: 1px solid var(--color-line);
	box-shadow: var(--shadow-strip);
	font-size: var(--fs-meta);
	font-weight: 700;
	letter-spacing: var(--track-label);
	margin: 0;
	padding: 6px 10px;
	text-transform: uppercase;
}

.panel-body {
	display: grid;
	gap: 14px;
	padding: 16px;
}

.stack {
	display: grid;
	gap: 14px;
}

.rowline {
	align-items: end;
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
}

/* Forms. */

.field {
	display: grid;
	gap: 4px;
	min-width: 0;
}

.field-label {
	color: var(--color-muted);
	font-size: var(--fs-meta);
	letter-spacing: var(--track-label);
	text-transform: uppercase;
}

.input,
.select {
	appearance: none;
	background: var(--color-field);
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-sunken);
	color: var(--color-ink);
	outline: none;
	padding: 7px 9px;
	width: 100%;
}

.select {
	background-image:
		linear-gradient(45deg, transparent 50%, var(--color-muted) 50%),
		linear-gradient(135deg, var(--color-muted) 50%, transparent 50%);
	background-position: right 10px center, right 6px center;
	background-repeat: no-repeat;
	background-size: 4px 4px;
	padding-right: 24px;
}

.input:disabled,
.select:disabled {
	background: var(--color-panel);
	color: var(--color-muted);
}

.check {
	align-items: center;
	cursor: pointer;
	display: flex;
	gap: 8px;
	padding-block: 7px 0;
}

.check input {
	appearance: none;
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-sunken);
	flex: none;
	height: 15px;
	width: 15px;
}

.check input:checked {
	background: var(--color-ink);
	box-shadow: inset 0 0 0 2px var(--color-field);
}

/* The keyboard ring is the marching ants these machines actually drew: a 1px
   dashed line, hairline enough not to compete with the control and dashed
   rather than dotted because beads at this size read as dots of ink rather than
   as a line. It sits outside the control, so it never collides with the 1px
   border the control already has, and the gap of panel between the two is why
   one ink colour serves the light controls and the near-black primary button
   alike, 11:1 against the panel it lands on. At AA only SC 2.4.7 binds, and a
   visible indicator satisfies it; the 2 CSS pixel perimeter area of SC 2.4.13
   is AAA, and a broken line does not meet it, so this rule is what changes if
   AAA ever becomes the target. The sheet has no way to focus an input, so
   .is-focus draws the ring on demand.
   */
:focus-visible,
.input.is-focus {
	outline: 1px dashed var(--color-ink);
	outline-offset: 2px;
}

/* justify-self so a button in a grid body keeps its own width rather than
   filling the column. */
.btn {
	background: var(--color-panel);
	border: 1px solid var(--color-line);
	box-shadow: var(--shadow-bevel);
	color: var(--color-ink);
	cursor: pointer;
	justify-self: start;
	padding: 7px 14px;
	text-decoration: none;
}

.btn:hover:not(:disabled) {
	background: var(--color-field);
}

.btn:active:not(:disabled) {
	box-shadow: var(--shadow-bevel-in);
}

.btn:disabled {
	border-color: var(--color-muted);
	color: var(--color-muted);
	cursor: not-allowed;
}

.btn.is-primary {
	background: var(--color-accent);
	color: var(--color-accent-ink);
}

.btn.is-primary:hover:not(:disabled) {
	background: var(--color-accent-hi);
}

.btn.is-ghost {
	background: transparent;
}

.btn.is-ghost:hover:not(:disabled) {
	background: var(--color-field);
}

/* Messages, data and the furniture of the specimen sheet. */

/* The box names itself: colour alone would leave a greyscale page with nothing
   to tell an error from a note, and red against green is the pairing colour
   blindness cannot separate. */
.alert {
	border: 1px solid var(--color-line);
	list-style: none;
	margin: 0;
	padding: 8px 10px;
}

.alert ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.alert li + li {
	margin-top: 3px;
}

.alert-label {
	font-size: var(--fs-meta);
	font-weight: 700;
	letter-spacing: var(--track-label);
	margin: 0 0 4px;
	text-transform: uppercase;
}

.alert.is-error {
	background: var(--color-danger-field);
	border-color: var(--color-danger);
	color: var(--color-danger);
}

.alert.is-success {
	background: var(--color-success-field);
	border-color: var(--color-success);
	color: var(--color-success);
}

.alert.is-info {
	background: var(--color-field);
	color: var(--color-ink);
}

.meta {
	color: var(--color-muted);
	font-size: var(--fs-meta);
}

.rows {
	display: grid;
	gap: 5px;
	margin: 0;
}

.row {
	align-items: baseline;
	display: grid;
	gap: 10px;
	grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
}

.rows .row + .row {
	border-top: 1px dotted var(--color-muted);
	padding-top: 5px;
}

.row-key {
	color: var(--color-muted);
	font-size: var(--fs-meta);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.row-val {
	margin: 0;
	overflow-wrap: anywhere;
}

.table {
	border-collapse: collapse;
	width: 100%;
}

.table th,
.table td {
	border-bottom: 1px solid var(--color-line);
	padding: 6px 8px;
	text-align: left;
}

.table thead th {
	border-bottom-width: 2px;
	color: var(--color-muted);
	font-size: var(--fs-meta);
	letter-spacing: var(--track-label);
	text-transform: uppercase;
}

.tag {
	border: 1px solid currentColor;
	display: inline-block;
	font-size: var(--fs-meta);
	letter-spacing: 0.06em;
	padding: 1px 6px;
	text-transform: uppercase;
}

.tag.is-ok {
	color: var(--color-success);
}

.tag.is-warn {
	color: var(--color-muted);
}

.tag.is-bad {
	color: var(--color-danger);
}

.link {
	color: var(--color-accent);
	text-decoration: underline;
	text-underline-offset: 2px;
}

@media (max-width: 40rem) {
	.desk {
		padding: 10px;
	}

	.row {
		grid-template-columns: minmax(0, 1fr);
		gap: 0;
	}
}
