Three sites, one directory each
flux.vision, tobias.raayoni-last.com and randogoth.com. They were one site until October 2026, serving four audiences from a single dated list, which is the only format that can hold unrelated things at once. flux.vision is recovered from the Wayback Machine, where it had sat since being deleted in 2021: 90 articles, closed and maintained rather than updated. tobias.raayoni-last.com is the professional record, 97 articles back to 1999. randogoth.com is the technical work, 41 articles across six areas, served as an ANSI bulletin board. History starts here deliberately. The development history, the 266-row inventory that records why each article went where it did, the Wayback pipelines, the pruned articles, the source archives and the full-size images are kept beside the repo rather than in it, so a clone is the web presence and not the argument that produced it. REFACTOR.md is gitignored for the same reason. See README.md for where that material lives, and keep it: the sort is not reproducible from these three directories alone. Each site documents itself in its own README, including the traps that cost time and look like nothing when you get them wrong.
This commit is contained in:
commit
8505b7e9e0
1003 changed files with 13289 additions and 0 deletions
191
randogoth.com/bbs/static/bbs.css
Normal file
191
randogoth.com/bbs/static/bbs.css
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* randogoth BBS
|
||||
*
|
||||
* An ANSI bulletin board, not a DOS application. The distinction is the whole
|
||||
* point: Turbo Vision is grey chrome, dialogs and a menu bar, which is what the
|
||||
* previous theme gave. A BBS is 80 columns of CP437 on black, the 16 colour
|
||||
* EGA palette, numbered areas with hotkeys, and a status line at the bottom.
|
||||
*
|
||||
* Everything is sized in `ch` so the screen stays exactly 80 columns wide at
|
||||
* any font size, the way a terminal does.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Classic Console';
|
||||
src: url('clacon2.woff2') format('woff2');
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
/* the IBM EGA/VGA text palette, unaltered */
|
||||
:root {
|
||||
--black: #000000; --blue: #0000aa; --green: #00aa00; --cyan: #00aaaa;
|
||||
--red: #aa0000; --magenta: #aa00aa; --brown: #aa5500; --grey: #aaaaaa;
|
||||
--dgrey: #555555; --lblue: #5555ff; --lgreen: #55ff55; --lcyan: #55ffff;
|
||||
--lred: #ff5555; --lmagenta: #ff55ff; --yellow: #ffff55; --white: #ffffff;
|
||||
}
|
||||
|
||||
html { background: var(--black); }
|
||||
body {
|
||||
margin: 0; padding: 0;
|
||||
background: var(--black);
|
||||
color: var(--grey);
|
||||
font-family: 'Classic Console', ui-monospace, monospace;
|
||||
/* clamp so 80ch always fits the viewport, phone to desktop */
|
||||
font-size: clamp(7px, 2.32vw, 17px);
|
||||
/* Exactly one cell per row. Each box row is its own <pre>, so any leading at
|
||||
all opens a gap between them and the vertical box-drawing characters stop
|
||||
meeting. Prose gets its leading back on .msgbody below. */
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: none;
|
||||
font-smooth: never;
|
||||
}
|
||||
|
||||
#screen { width: 80ch; margin: 0 auto; padding: 1.2em 0 2.4em; }
|
||||
|
||||
pre, .row { margin: 0; font: inherit; white-space: pre; }
|
||||
|
||||
/* ── the masthead ──────────────────────────────────────────────────────── */
|
||||
/* The masthead is TheDraw's "Syndicate", an ANSI font from the board era, baked
|
||||
in as static text. It uses exactly three colour levels, so the ramp is three
|
||||
custom properties and recolouring costs nothing. See bbs.js: the ramp is drawn
|
||||
at random once per session, which is the one piece of decoration on this site
|
||||
that is also its subject. */
|
||||
.logo { margin: 0 0 .2em; }
|
||||
/* Both planes carry tone. ANSI paints solid areas as a space with a background,
|
||||
so mapping only the foreground drops most of the mass: the first attempt at
|
||||
this lost 247 of 355 inked cells and the letters came out hollow. */
|
||||
.logo .f1 { color: var(--l1, #0000aa); }
|
||||
.logo .f2 { color: var(--l2, #00aa00); }
|
||||
.logo .f3 { color: var(--l3, #ffff55); }
|
||||
.logo .b1 { background: var(--l1, #0000aa); }
|
||||
.logo .b2 { background: var(--l2, #00aa00); }
|
||||
.logo .b3 { background: var(--l3, #ffff55); }
|
||||
.rule { color: var(--blue); }
|
||||
.sysline { color: var(--dgrey); }
|
||||
.sysline b { color: var(--grey); font-weight: normal; }
|
||||
|
||||
/* ── links behave like a lightbar: inverse on hover, as a BBS selection ─── */
|
||||
a { color: var(--lcyan); text-decoration: none; }
|
||||
a:hover, a:focus { background: var(--lcyan); color: var(--black); outline: none; }
|
||||
/* A lightbar inverts the whole cell. Spans inside a link carry their own colour
|
||||
(.area is cyan, .key is yellow) and would otherwise keep it against the cyan
|
||||
highlight, which left the area names cyan on cyan and invisible. */
|
||||
a:hover *, a:focus * { color: inherit; background: transparent; }
|
||||
a:visited { color: var(--lcyan); }
|
||||
|
||||
/* ── hotkeys ───────────────────────────────────────────────────────────── */
|
||||
.key { color: var(--yellow); }
|
||||
.key b { color: var(--white); font-weight: normal; }
|
||||
|
||||
/* ── area / message listings ───────────────────────────────────────────── */
|
||||
.box { color: var(--blue); }
|
||||
.boxtitle { color: var(--yellow); }
|
||||
.area { color: var(--lcyan); }
|
||||
.desc { color: var(--grey); }
|
||||
.count { color: var(--lgreen); }
|
||||
.date { color: var(--dgrey); }
|
||||
.sect { color: var(--brown); }
|
||||
.num { color: var(--dgrey); }
|
||||
.new { color: var(--lred); }
|
||||
|
||||
/* ── a message ─────────────────────────────────────────────────────────── */
|
||||
.msghdr { color: var(--dgrey); }
|
||||
.msghdr b { color: var(--lcyan); font-weight: normal; }
|
||||
.msghdr .subj { color: var(--yellow); }
|
||||
|
||||
.msgbody { color: var(--grey); margin: 1em 0 0; line-height: 1.4; }
|
||||
.msgbody p { margin: 0 0 1em; }
|
||||
.msgbody h1, .msgbody h2, .msgbody h3 {
|
||||
color: var(--yellow); font-size: 1em; font-weight: normal;
|
||||
text-transform: uppercase; margin: 1.6em 0 .6em; }
|
||||
.msgbody h1::before, .msgbody h2::before, .msgbody h3::before { content: "── "; color: var(--blue); }
|
||||
.msgbody ul, .msgbody ol { margin: 0 0 1em; padding-left: 3ch; }
|
||||
.msgbody li { margin: 0; }
|
||||
.msgbody ul { list-style: none; }
|
||||
.msgbody ul > li::before { content: "\00B7 "; color: var(--cyan); }
|
||||
/* Pictures keep the scanlines the page gave up: on a board, an image was a
|
||||
monitor showing it. See plugins/crtimages.py for the wrapper. */
|
||||
.msgbody .crt { position: relative; display: block; margin: 1em 0; line-height: 0;
|
||||
border: 1px solid var(--blue); }
|
||||
/* Everything fills the column, including images smaller than it. Upscaling is
|
||||
nearest-neighbour, so a 320 wide DOS screenshot comes out as bigger pixels
|
||||
rather than a blurred one, which is the right answer for this material. */
|
||||
.msgbody .crt img { display: block; width: 100%; height: auto; margin: 0;
|
||||
border: 0; image-rendering: pixelated; }
|
||||
/* The scanlines are the monitor it is being shown on. Look straight at it and
|
||||
they go, which is also the only way to see the picture clean. */
|
||||
.msgbody .crt::after { content: ""; position: absolute; inset: 0; pointer-events: none;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
rgba(0,0,0,0) 0 2px, rgba(0,0,0,.38) 2px 3px);
|
||||
opacity: 1; transition: opacity .12s linear; }
|
||||
.msgbody .crt:hover::after,
|
||||
.msgbody .crt:focus-within::after { opacity: 0; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.msgbody .crt::after { transition: none; }
|
||||
}
|
||||
/* Embeds fill the column like everything else. The ratio is injected per
|
||||
element by plugins/crtimages.py from each embed's own attributes. */
|
||||
.msgbody iframe { display: block; width: 100%; height: auto; margin: 1em 0;
|
||||
border: 1px solid var(--blue); }
|
||||
.msgbody blockquote { margin: 0 0 1em; padding-left: 2ch; color: var(--lgreen);
|
||||
border-left: 1px solid var(--green); }
|
||||
/* Code blocks only. A bare :not() would have been enough until a page put box
|
||||
drawing in the body: the frames are <pre> too, and were being wrapped, boxed
|
||||
and turned green as if they were source. */
|
||||
.msgbody code { color: var(--lgreen); font: inherit; }
|
||||
.msgbody pre:not([class]), .msgbody pre.highlight {
|
||||
color: var(--lgreen); font: inherit; white-space: pre-wrap;
|
||||
border: 1px solid var(--blue); padding: .4em 1ch; margin: 0 0 1em;
|
||||
overflow-x: auto; }
|
||||
.msgbody strong { color: var(--white); font-weight: normal; }
|
||||
.msgbody em { color: var(--lcyan); font-style: normal; }
|
||||
.msgbody hr { border: 0; border-top: 1px solid var(--blue); margin: 1.4em 0; }
|
||||
.msgbody a { text-decoration: underline; }
|
||||
|
||||
/* the Fidonet tagline that closed every message */
|
||||
.tagline { color: var(--dgrey); margin-top: 1.6em; }
|
||||
.tagline .origin { color: var(--green); }
|
||||
|
||||
/* ── the prompt. A BBS always leaves you at one. ───────────────────────── */
|
||||
.prompt { color: var(--grey); margin-top: 1.4em; }
|
||||
.prompt .cursor {
|
||||
background: var(--grey); color: var(--grey);
|
||||
animation: blink 1.06s steps(1) infinite; }
|
||||
@keyframes blink { 50% { background: transparent; } }
|
||||
@media (prefers-reduced-motion: reduce) { .prompt .cursor { animation: none; } }
|
||||
|
||||
|
||||
/* the keyword cloud is the one thing that cannot hold an 80 column frame */
|
||||
.cloud { color: var(--grey); padding: .4em 2ch .6em; line-height: 1.6; }
|
||||
.cloud a { margin-right: .4ch; }
|
||||
.cloud .num { margin-right: 1.4ch; }
|
||||
|
||||
/* pages */
|
||||
.bigmail { margin: 0 0 1.4em; }
|
||||
.bigmail a, .mail { color: var(--yellow); }
|
||||
.bigmail a:hover { background: var(--yellow); color: var(--black); }
|
||||
.prompt .hint { color: var(--dgrey); }
|
||||
|
||||
|
||||
/* guestbook */
|
||||
.gb-rule { margin: 1.5em 0 0; }
|
||||
/* Box drawing is on the grid wherever it appears, including inside a page body,
|
||||
which otherwise hands it the 1.4 leading that prose wants. */
|
||||
.msgbody .box, .msgbody .gb-rule, .msgbody .rule { line-height: 1; }
|
||||
.gb-body { color: var(--grey); line-height: 1.4; padding: .5em 0 0 4ch; }
|
||||
|
||||
/* The form does not try to close each line with a vertical: a text input is not
|
||||
a character cell and will not line up with one. Top and bottom rules only. */
|
||||
.gb-form { margin: 0; padding: .8em 0 .4em; }
|
||||
.gb-row { margin: 0 0 .7em; padding-left: 2ch; line-height: 1.6; }
|
||||
.gb-row label { display: inline-block; width: 10ch; color: var(--dgrey); }
|
||||
.gb-row label.second { width: auto; padding: 0 1ch 0 3ch; }
|
||||
.gb-form input, .gb-form textarea {
|
||||
font: inherit; background: #000; color: var(--white);
|
||||
border: 1px solid var(--blue); outline: 0; padding: 0 1ch; }
|
||||
.gb-form input { width: 22ch; }
|
||||
.gb-form textarea { width: 58ch; vertical-align: top; resize: vertical; line-height: 1.4; }
|
||||
.gb-form ::placeholder { color: var(--dgrey); }
|
||||
.gb-form input:focus, .gb-form textarea:focus { border-color: var(--lcyan); color: var(--lcyan); }
|
||||
.gb-form button { font: inherit; background: none; border: 0; padding: 0;
|
||||
color: var(--yellow); cursor: pointer; }
|
||||
.gb-form button:hover { background: var(--yellow); color: var(--black); }
|
||||
Loading…
Add table
Add a link
Reference in a new issue