/* 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 {
  /* A board is black by definition; tell the browser so the canvas between
     documents and the scrollbar match instead of flashing light. */
  color-scheme: dark;
  --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;
}

/* ── paper ──────────────────────────────────────────────────────────────
 * Light mode is a remap of the sixteen slots above, not a second stylesheet.
 * Every rule in this file goes through those variables, so the whole board
 * inverts by redefining them and nothing below needs to know.
 *
 * Warm greys on sepia, monochrome: this is not an EGA palette and does not
 * pretend to be one. It is the amber-on-paper end of the same machine. The
 * tones are picked by contrast ratio against the ground rather than by eye,
 * and the ordering of the sixteen is preserved, so what was recessive in the
 * dark theme stays recessive here: --dgrey is quiet, --blue is chrome,
 * --yellow carries the hotkeys, --lred is the loudest thing on the screen.
 *
 * Unlike the dark palette these are allowed to depart from EGA fidelity, so
 * --dgrey is lifted to 4.5:1 rather than left at the 2.7:1 its counterpart
 * sits at on black.
 */
[data-theme="light"] {
  color-scheme: light;
  --black:   #f4ecdc;  /*  1.0  the paper                       */
  --blue:    #a0917c;  /*  2.6  frames and rules                */
  --brown:   #887a64;  /*  3.6  section marks                   */
  --lblue:   #a0917c;  /*  2.6                                  */
  --dgrey:   #756b5b;  /*  4.5  dates, numbers, the sysline     */
  --green:   #6a6053;  /*  5.2  quote rule                      */
  --cyan:    #655c50;  /*  5.6  list bullets                    */
  --magenta: #655c50;  /*  5.6                                  */
  --red:     #534d43;  /*  7.1                                  */
  --lgreen:  #534d43;  /*  7.1  code, counts, quoted text       */
  --grey:    #47423a;  /*  8.5  body                            */
  --lcyan:   #38342e;  /* 10.5  links and the lightbar          */
  --lmagenta:#38342e;  /* 10.5                                  */
  --yellow:  #2b2824;  /* 12.5  headings, hotkeys, the address  */
  --white:   #1e1c19;  /* 14.5  emphasis                        */
  --lred:    #121210;  /* 16.0  new                             */
}

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 */
  --cell: clamp(7px, 2.32vw, 17px);
  font-size: var(--cell);
  /* 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; }

/* ── mobile: read content is one size, not two ───────────────────────────
 * 80 real characters cannot be legible and fit a phone's width at the same
 * time, so below this breakpoint every piece of actual content (prose,
 * the message header, the listing tables) renders at one fixed size
 * instead of inheriting --cell. Box-drawn tables that packed two columns
 * into 80 characters get a second, narrower rendering built for this
 * width instead: see .desktop-only/.mobile-only below, toggled by the
 * matching (min-width: 601px) block further down. The masthead logo and
 * the plain ──── rule dividers are graphics, not content, and are left on
 * --cell exactly as before.
 */
@media (max-width: 600px) {
  .msgbody,
  .cloud,
  .pagehdr,
  .sysline,
  .tagline,
  .prompt,
  .mobile-only { font-size: 1rem; }

  /* The desktop article header packs two fields per row with hand-counted
     padding; the mobile rows (and a page's single Subj row) are one field
     each, so they just need to wrap like normal text instead of running
     off the edge under white-space: pre. The sysline, tagline and the
     bottom command bar are actual words, not a repeating-character rule,
     so they wrap too. */
  .msghdr.mobile-only,
  .pagehdr,
  .sysline,
  .tagline,
  .prompt { white-space: normal; }

  /* Box drawing embedded in prose (a pull-quote box, if one is ever used)
     is a rigid character grid and must stay at the chrome size or its
     columns stop lining up against their own border characters. */
  .msgbody .box, .msgbody .gb-rule, .msgbody .rule { font-size: var(--cell); }

  .desktop-only { display: none; }
}
@media (min-width: 601px) {
  .mobile-only { display: none; }
}

/* 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: var(--black); 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); }
