/* Deed, dressed like blahg: iA Writer Mono on cream, a sticky centred nav,
   and links whose underlines wear the four accent colours at random.
   The four are the custom properties below, and `assets/color.js` reads them
   from here, so the only part written there is which one a link gets. */

/* The typeface is committed rather than fetched, like the compiler: the site
   promises no third-party requests, and a release that moves on cannot be
   allowed to move this page with it. iA Writer Mono is SIL-1.1; the licence is
   committed beside the files in assets/fonts/.

   `optional` rather than `swap`, and the two upright faces are preloaded in
   every page's head. `swap` paints the fallback first and then re-lays the
   page out under the reader, which is what a hard reload used to look like.
   `optional` is the one value that cannot do that: the face either arrives in
   time to be the first thing drawn, or it waits for the next visit. */

@font-face {
  font-family: "iA Writer Mono";
  src: url("fonts/ia-writer-mono-latin-400-normal.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: optional;
}

@font-face {
  font-family: "iA Writer Mono";
  src: url("fonts/ia-writer-mono-latin-400-italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 400;
  font-display: optional;
}

@font-face {
  font-family: "iA Writer Mono";
  src: url("fonts/ia-writer-mono-latin-700-normal.woff2") format("woff2");
  font-style: normal;
  font-weight: 700;
  font-display: optional;
}

@font-face {
  font-family: "iA Writer Mono";
  src: url("fonts/ia-writer-mono-latin-700-italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 700;
  font-display: optional;
}

:root {
  --white: #efefef;
  --transwhite: #efefefb3;
  --moretranswhite: #efefef1a;
  --black: #252525;
  --transblack: #252525b3;
  --moretransblack: #2525251a;
  --gray: #6b6b6b;
  --red: #e4002b;
  --green: #24d05a;
  --pink: #eb4888;
  --blue: #10a2f5;
  --yellow: #e9bc3f;
  /* The palette is for decoration; text in it has to be readable on the
     cream, so the semantic colours are the palette darkened to pass. */
  --error: #c20022;
  --warning: #8a6100;
  --ok: #157a33;
  --syntax-keyword: #0f6fb8;
  --syntax-string: #b02e63;
  --syntax-number: #157a33;
  --syntax-comment: #6b6b6b;
  --syntax-punctuation: #252525;
  /* The console is black against the cream, and a little lighter against the
     black, so it is a screen in both themes. */
  --console: #252525;
  --mono: "iA Writer Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, "Liberation Mono", monospace;
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: light dark;
}

html,
body {
  margin: auto;
  padding: 20px;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

body {
  max-width: none;
  background: var(--white);
  color: var(--black);
  font-size: 16px;
  line-height: 1.6;
}

/* Kept as a hook: every page now uses the whole width, and this one used to
   be the only one allowed to. */
body.wide {
  max-width: none;
}

html,
body,
button,
input,
select,
textarea,
code {
  font-family: var(--mono);
}

a {
  color: var(--black);
  text-decoration: underline;
  text-underline-offset: 0.3ex;
  text-decoration-thickness: 0.3ex;
}

a:focus-visible,
button:focus-visible,
textarea:focus-visible,
select:focus-visible,
input:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

h1,
h2 {
  font-weight: 400;
}

h1,
strong,
b {
  color: var(--black);
}

h2,
h3,
h4,
h5,
h6 {
  color: var(--gray);
  line-height: 1.1;
}

code {
  background-color: var(--moretransblack);
  padding: 2px 5px;
  font-weight: 700;
}

pre {
  padding: 1rem;
}

pre.code {
  margin: 0;
  padding: 1rem;
  background: var(--moretransblack);
  font-size: 0.9rem;
  line-height: 1.5;
  overflow-x: auto;
}

pre.code code {
  background: none;
  padding: 0;
  font-weight: 400;
}

pre.code b {
  color: var(--syntax-keyword);
}

/* Header: the wordmark and its tagline, centred like the rest. */

header.site {
  display: block;
}

header.site .header-section {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

header.site .header-frame {
  max-width: none;
  text-align: center;
}

header.site h1 {
  margin: 0;
  font-size: 2em;
}

/* The bracket is drawn in currentColor, so the mark follows the wordmark into
   dark mode without a second file to keep in step. */
header.site h1 .mark {
  width: 1.1em;
  height: 1.1em;
  vertical-align: -0.16em;
  margin-right: 0.3em;
}

header.site h1 a {
  text-decoration: none;
}

header.site h1 a {
  text-decoration: none;
}

header.site h2 {
  margin: 0.5em 0;
}

/* The nav is sticky and sits on a blur of the page, so it stays readable
   while the content scrolls under it. */

nav.site {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0 0 1.5rem;
  padding: 0 4px;
  text-align: center;
  background: var(--transwhite);
  backdrop-filter: blur(1px);
}

nav.site a {
  display: inline-block;
  margin: 0.75rem 0;
  text-decoration: none;
}

nav.site a:not(:last-child) {
  padding-right: 12px;
}

nav.site a.active {
  font-weight: 700;
  text-underline-offset: 0.3ex;
  text-decoration: underline 0.3ex;
}

@media (max-width: 600px) {
  nav.site a:not(:last-child) {
    padding-right: 0;
  }
}

main {
  line-height: 1.6;
}

main h1 {
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.4rem);
  margin: 0 0 0.5rem;
}

.lede {
  color: var(--gray);
  margin: 0 0 1.5rem;
}

/* Landing */

/* The header above is centred, and main used to start by going back to the
   left edge. Headings and the short lines that report something are centred
   with it; the prose and the source under them are not, because a centred
   paragraph is a paragraph nobody finds the start of. Scoped to main's own
   children, so a heading inside a pane or a diagnostic is left where it is. */
main > h1,
main > h2,
main > h3,
main > .lede,
main > .status,
main > .summary {
  text-align: center;
}

main > .picker,
main > .controls,
.walk {
  justify-content: center;
}

.show {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  margin: 2rem 0;
}

/* Inside a grid cell the page has already decided the width. */
.show pre.code {
  padding: 0.75rem;
  max-width: 100%;
}

/* A grid item is as wide as its widest line unless told otherwise, so a long
   line of source pushes the page sideways instead of scrolling its own box. */
.show > div {
  min-width: 0;
}

.show h2 {
  font-size: 0.95rem;
  margin: 0 0 0.5rem;
}

.walk {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75rem;
  margin: 1.5rem auto 0;
}

.walk .summary {
  margin: 0;
}

[data-step] {
  scroll-margin-top: 2rem;
  transition: color 0.2s;
}

[data-step].here {
  color: var(--syntax-string);
}

@media (prefers-reduced-motion: reduce) {
  [data-step] {
    transition: none;
  }
}

/* The next cards are the blog's post list: a big title over a short
   excerpt, no boxes. */

.next {
  margin: 2.5rem auto 0;
}

.next .card {
  display: block;
  margin: 1.5rem 0;
  padding-left: 1rem;
  border-left: 3px solid var(--green);
}

.next .card:nth-of-type(4n + 2) {
  border-left-color: var(--pink);
}

.next .card:nth-of-type(4n + 3) {
  border-left-color: var(--blue);
}

.next .card:nth-of-type(4n + 4) {
  border-left-color: var(--yellow);
}

.next .card strong {
  display: block;
  font-size: 1.3em;
  font-weight: 400;
  margin-bottom: 0.25rem;
}

.next .card span {
  color: var(--gray);
  font-size: 0.9rem;
}

/* Two columns hold a seventy-two character line comfortably now that the page
   is the whole window, so they only stack where a window is narrow. */
@media (max-width: 52rem) {
  .show {
    grid-template-columns: 1fr;
  }
}

/* Playground */

/* The picker and the verbs on one line, above the editor, so what is left of
   the window belongs to the thing people came here to type in. */
.controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  margin: 0 0 0.75rem;
}

.verbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

.picker {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0;
}

.picker label {
  color: var(--gray);
  font-size: 0.9rem;
}

select,
input[type="search"],
button {
  font: inherit;
  font-size: 0.95rem;
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--gray);
  border-radius: 0;
  background: var(--white);
  color: var(--black);
}

button {
  cursor: pointer;
}

/* Held at the width of its own label, because it swaps that label for what it
   just did and a button that resizes moves the row it is in. Nineteen is the
   longest of the three, and the face is monospaced, so `ch` is exact. */
#share {
  min-width: calc(19ch + 1.6rem + 2px);
}

button:hover:not(:disabled),
select:hover:not(:disabled) {
  border-color: var(--black);
}

button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* A verb nobody pressed still has to hold the compiler for the moment it
   takes, but it must not say so: four buttons fading and coming back on every
   keystroke is the row looking broken. */
.controls.quiet [data-verb]:disabled {
  opacity: 1;
}

.summary {
  color: var(--gray);
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

.summary:empty {
  display: none;
}

.status {
  color: var(--gray);
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

.panes {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* The editor: a gutter, the colouring, and the textarea over the top of it.
   One source line is one visual line (no wrapping) so the gutter can say
   which line it is without lying about wrapped ones. */

.editor {
  display: flex;
  /* Fixed rather than a minimum: both layers inside are positioned, so the
     only thing that could give this box a height is the gutter, and then the
     editor would be as tall as the file. */
  height: 26rem;
  border: 1px solid var(--gray);
  background: var(--white);
  overflow: hidden;
}

.editor .gutter,
.editor .highlight,
.editor textarea#source {
  font-family: var(--mono);
  font-size: 0.9rem;
  line-height: 1.5;
  tab-size: 4;
  white-space: pre;
}

.editor .gutter {
  flex: 0 0 auto;
  padding: 0.75rem 0.5rem 0.75rem 0.75rem;
  color: var(--gray);
  text-align: right;
  user-select: none;
  overflow: hidden;
  border-right: 1px solid var(--gray);
}

.editor .gutter b {
  font-weight: 600;
}

.editor .gutter b.has-error {
  color: var(--error);
}

.editor .gutter b.has-warning {
  color: var(--warning);
}

.editor .layers {
  position: relative;
  flex: 1;
  min-width: 0;
}

.editor .highlight,
.editor textarea#source {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0.75rem;
  border: 0;
  border-radius: 0;
  min-height: 0;
  overflow: auto;
}

.editor .highlight {
  pointer-events: none;
  color: var(--syntax-punctuation);
  /* Driven by the textarea's scroll position, so it must not grow one of its
     own: two scrollbars in the same box is one too many. */
  overflow: hidden;
}

.editor textarea#source {
  width: 100%;
  height: 100%;
  resize: none;
  background: transparent;
  /* The colouring underneath is what anyone reads; this layer is the caret,
     the selection and the keyboard. */
  color: transparent;
  caret-color: var(--black);
}

.editor textarea#source::selection {
  background: color-mix(in srgb, var(--blue) 30%, transparent);
}

.t-keyword {
  color: var(--syntax-keyword);
  font-weight: 600;
}

.t-string {
  color: var(--syntax-string);
}

.t-number {
  color: var(--syntax-number);
}

.t-comment {
  color: var(--syntax-comment);
  font-style: italic;
}

.t-punctuation {
  color: var(--syntax-punctuation);
}

.t-error {
  color: var(--error);
  text-decoration: underline wavy;
}

/* The console is where the compiler answers, in both themes: a bar on top
   and the answer scrolling beneath it, like the playgrounds of the
   languages that had this idea first.
   It is there from the start, at the size it will stay. Growing it when a
   verb was pressed meant the editor changed shape under the cursor, every
   time, and the thing that moved was the text somebody was reading. */

.console {
  display: flex;
  flex-direction: column;
  height: 14rem;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--gray);
  background: var(--console);
  color: var(--white);
}

.console .bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  border-bottom: 1px solid var(--gray);
  color: var(--gray);
  font-size: 0.8rem;
}

.console .bar .dots {
  display: inline-flex;
  gap: 0.3rem;
}

/* Three of the site's four, in the order everything else uses them, rather
   than the traffic light every terminal window borrowed. They breathe on a
   stagger so the console reads as something running, and breathe faster while
   it is actually answering. */

.console .bar .dots i {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  animation: breathe 2.4s ease-in-out infinite;
}

@keyframes breathe {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

.console.working .bar .dots i {
  animation-duration: 0.7s;
}

@media (prefers-reduced-motion: reduce) {
  .console .bar .dots i {
    animation: none;
  }
}

.console .bar .dots i:nth-child(1) {
  background: var(--green);
}

.console .bar .dots i:nth-child(2) {
  background: var(--pink);
  animation-delay: 0.3s;
}

.console .bar .dots i:nth-child(3) {
  background: var(--blue);
  animation-delay: 0.6s;
}

.console #output {
  flex: 1;
  min-height: 0;
  margin: 0;
  padding: 0.75rem;
  border: 0;
  background: transparent;
  color: var(--white);
  font-family: var(--mono);
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow-y: auto;
}

/* What the compiler said, in the colours it means. */

.d-error {
  color: var(--error);
  font-weight: 700;
}

.d-warning {
  color: var(--warning);
  font-weight: 700;
}

.d-gutter,
.d-note {
  color: var(--gray);
}

.d-caret {
  color: var(--error);
}

.d-ok {
  color: var(--ok);
}

/* The console is black in both themes, so what the compiler says inside it
   is told in the colours that pass on black. */

.console .d-error,
.console .d-caret {
  color: #f2777a;
}

.console .d-warning {
  color: #e6b455;
}

.console .d-gutter,
.console .d-note {
  color: #a4a4a4;
}

.console .d-ok {
  color: #24d05a;
}

#output button.apply {
  font-family: var(--mono);
  font-size: 0.8rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--black);
  text-decoration: underline;
  cursor: pointer;
}

.console #output button.apply {
  color: var(--white);
}

#output button.apply:hover {
  border-color: transparent;
}

/* The playground in one screen, laid out like the ones that fit a window:
   the controls in a strip, the editor taking everything left over, and the
   answer beside it rather than under it. A console under the editor costs a
   third of the height of the thing people came here to type in.
   Only where there is room for it: on a phone, or a short window, scrolling
   is the right answer and a 26rem editor squeezed into 300 pixels is not. */
@media (min-width: 52rem) and (min-height: 40rem) {
  body.fits {
    max-width: none;
    /* The page's own padding is outside this box, so a whole viewport here is
       a viewport and a bit, and the bit is a scrollbar. */
    height: calc(100dvh - 40px);
    overflow: hidden;
  }

  body.fits .wrap {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding-bottom: 1rem;
  }

  body.fits main {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.fits nav.site {
    margin-bottom: 0.75rem;
  }

  body.fits nav.site a {
    margin: 0.4rem 0;
  }

  body.fits header.site h1 {
    font-size: 1.3rem;
  }

  /* The tagline is the landing page's sentence, and the wordmark still says
     whose playground this is. */
  body.fits header.site h2 {
    display: none;
  }

  body.fits main h1 {
    font-size: 1.3rem;
  }

  /* The nav already says which page this is, and the sentence under it is on
     the landing page. In here the screen belongs to the editor. */
  body.fits main h1,
  body.fits main .lede {
    display: none;
  }

  body.fits .lede,
  body.fits .status,
  body.fits .picker,
  body.fits .verbs {
    margin-bottom: 0.5rem;
  }

  /* Reading width costs a line here, and the line is the editor's. */
  body.fits main .lede {
    max-width: none;
  }

  /* An example's own comment is however long its author wanted it. */
  body.fits .summary {
    margin-bottom: 0.5rem;
    max-height: 2.9em;
    overflow-y: auto;
  }

  body.fits .panes {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr minmax(24rem, 32%);
    gap: 1rem;
  }

  body.fits .editor {
    height: auto;
    min-height: 0;
  }

  body.fits textarea#source {
    height: 100%;
    min-height: 0;
    resize: none;
    overflow: auto;
  }

  body.fits .console {
    height: auto;
    min-height: 0;
  }

  /* One line of footer. The other paragraph is about the four verbs, which
     are now a hand's width above it. */
  body.fits footer.site p:first-child {
    display: none;
  }

  body.fits footer.site {
    margin-top: 0.5rem;
    padding: 0.4rem 0 0;
    font-size: 0.8rem;
  }

  body.fits footer.site p {
    margin: 0;
  }
}

/* Present to a screen reader, absent to everyone else. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

footer.site {
  margin-top: 2rem;
  padding: 25px;
  text-align: center;
  color: var(--gray);
  font-size: 0.8rem;
}

footer.site p {
  margin: 0.25rem 0;
}

/* Install */

main h2 {
  font-size: 1.2rem;
  margin: 2.5rem 0 0.75rem;
}

main h3 {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 1.5rem 0 0.5rem;
}

table.files {
  border-collapse: collapse;
  margin: 0 0 1rem;
  width: 100%;
}

table.files th,
table.files td {
  text-align: left;
  padding: 0.4rem 1.5rem 0.4rem 0;
  border-bottom: 1px solid var(--gray);
  vertical-align: top;
}

table.files th {
  color: var(--gray);
  font-weight: 600;
  font-size: 0.9rem;
}

/* The four accents were only doing the link underlines. They cycle through
   the things a page is made of as well now, in the same order everywhere, and
   still meaning nothing: which colour a section gets is which section it is,
   not what it says. */

main > h2::before {
  content: "";
  display: block;
  width: 2.5rem;
  height: 3px;
  margin: 0 auto 0.7rem;
  background: var(--green);
}

main > h2:nth-of-type(4n + 2)::before {
  background: var(--pink);
}

main > h2:nth-of-type(4n + 3)::before {
  background: var(--blue);
}

main > h2:nth-of-type(4n + 4)::before {
  background: var(--yellow);
}

table.files th {
  border-bottom-color: var(--blue);
}

.trouble dt {
  margin-top: 1.25rem;
  font-weight: 600;
}

.trouble dd {
  margin: 0.25rem 0 0;
  color: var(--gray);
}

/* Diagnostics index */

.diagnostic {
  border-top: 2px solid var(--green);
  padding-top: 1rem;
  margin-top: 1.5rem;
}

.diagnostic:nth-of-type(4n + 2) {
  border-top-color: var(--pink);
}

.diagnostic:nth-of-type(4n + 3) {
  border-top-color: var(--blue);
}

.diagnostic:nth-of-type(4n + 4) {
  border-top-color: var(--yellow);
}

.diagnostic h2 {
  font-size: 1rem;
  font-family: var(--mono);
  margin: 0 0 0.5rem;
  scroll-margin-top: 1rem;
}

.diagnostic h2 a {
  color: inherit;
}

.diagnostic .from {
  font-size: 0.8rem;
  font-family: var(--mono);
  margin-top: 0.4rem;
}

input[type="search"] {
  min-width: min(28rem, 100%);
}

/* Dark mode comes last, so it overrides what the light rules decided above.
   Everything that carries an explicit background here has to be told about
   the other side of the switch. */

@media (prefers-color-scheme: dark) {
  :root {
    --gray: #a4a4a4;
    --error: #f2777a;
    --warning: #e6b455;
    --ok: #24d05a;
    --syntax-keyword: #10a2f5;
    --syntax-string: #eb4888;
    --syntax-number: #24d05a;
    --syntax-comment: #a4a4a4;
    --syntax-punctuation: #efefef;
    --console: #2e2e2e;
  }

  ::selection {
    color: var(--white);
    background: #0f0f0f;
  }

  html,
  body {
    background: var(--black);
    color: var(--white);
  }

  body h1,
  body strong,
  body b {
    color: var(--white);
  }

  body a {
    color: var(--white);
  }

  body button,
  body select,
  body input {
    background: var(--black);
    color: var(--white);
  }

  body code,
  pre.code {
    background: var(--moretranswhite);
  }

  .editor {
    background: var(--black);
  }

  .editor textarea#source {
    caret-color: var(--white);
  }

  #output button.apply {
    color: var(--white);
  }

  nav.site {
    background: var(--transblack);
  }
}
