/* The Curly Message Format's site.
 *
 * The palette is brand/README.md's, both of them: a ground carries its own
 * theme colour, and that value is the whole of the colour on a page — save
 * code, which spends a set of its own because there the colour is the
 * content. Nothing here needs JavaScript but the playground. */

:root {
  --ground: #f5f5f3;
  --surface: #eaeae8;
  --hairline: #d6d6d5;
  --base: #14161a;
  --dim: #6b7079;
  --theme: #ff4365;

  --display: "Outfit", ui-sans-serif, system-ui, sans-serif;
  --body: "Literata", Georgia, "Times New Roman", serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --measure: 68ch;
  --gutter: clamp(20px, 4vw, 40px);

  /* What code is coloured with. One hue per part a placeholder is made of,
   * and a sixth for an escape, which stands outside the parts because it can
   * appear in any of them; the other languages the site writes are drawn in
   * the same six, so a page spends no colour it had not already spent. */
  --ink-brace: #b4283f;
  --ink-key: #1d5c9e;
  --ink-modifier: #6f3aa8;
  --ink-option-key: #0d6553;
  --ink-escape: #96580f;
  --ink-ph: rgb(20 22 26 / 7%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #14161a;
    --surface: #1f2125;
    --hairline: #343538;
    --base: #f5f5f3;
    --dim: #8a8f98;
    --theme: #f2a61a;

    --ink-brace: #ff8095;
    --ink-key: #79b8f3;
    --ink-modifier: #c5a3f0;
    --ink-option-key: #57c6a4;
    --ink-escape: #e8ae5c;
    --ink-ph: rgb(245 245 243 / 10%);
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

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

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--base);
  font-family: var(--body);
  font-size: 17.5px;
  line-height: 1.72;
  text-rendering: optimizeLegibility;
}

:focus-visible {
  outline: 2px solid var(--theme);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--surface);
  border: 1px solid var(--hairline);
  padding: 10px 16px;
  font-family: var(--display);
}

.skip:focus {
  left: var(--gutter);
  top: 12px;
}

/* Masthead ---------------------------------------------------------------- */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 32px;
  max-width: 1180px;
  margin: 0 auto;
  padding: 22px var(--gutter);
  border-bottom: 1px solid var(--hairline);
}

/* The whole lockup on one line: the wordmark without its tagline, then the
 * tagline beside it rather than under it, broken over two lines so the pair
 * stands as tall as the wordmark does. Its setting is the one the brand
 * records — capitals in Outfit at 0.22 em of letter spacing — and the three
 * colours are the plate's: brace in the theme colour, letters in the base,
 * tagline dimmed. */
.mark {
  display: inline-flex;
  align-items: center;
  gap: 13px;
  color: var(--base);
  text-decoration: none;
}

.wordmark {
  height: 25px;
  width: auto;
  display: block;
  /* Clear space of half the letter stroke, which the box does not carry. */
  margin-right: 1px;
}

.wordmark .brace {
  fill: var(--theme);
}

.wordmark .ink {
  fill: var(--base);
}

.mark .rule {
  width: 1px;
  height: 25px;
  background: var(--hairline);
}

.mark .tag {
  font-family: var(--display);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
  /* Centred on its ink rather than on its box: the box carries descender room
   * under the last baseline that the capitals never use, which lifts two
   * centred lines above the letters beside them. */
  transform: translateY(2px);
}

.mark:hover .tag {
  color: var(--base);
}

.masthead nav {
  margin-left: auto;
  min-width: 0;
  max-width: 100%;
}

/* The row scrolls sideways when it stops fitting rather than wrapping onto a
 * second line: a wrapped nav moves every item the moment the window changes
 * width. No scrollbar — it would sit under the items and take the row's whole
 * height with it. */
.masthead ul {
  display: flex;
  flex-wrap: nowrap;
  gap: 22px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.masthead ul::-webkit-scrollbar {
  display: none;
}

.masthead li {
  flex: none;
}

.masthead a {
  font-family: var(--display);
  white-space: nowrap;
  font-size: 15px;
  font-weight: 500;
  color: var(--dim);
  text-decoration: none;
  padding: 2px 0;
  border-bottom: 2px solid transparent;
}

.masthead a:hover {
  color: var(--base);
}

.masthead a[aria-current="page"] {
  color: var(--base);
  border-bottom-color: var(--theme);
}

.masthead .away a::after {
  content: " ↗";
  color: var(--dim);
}

/* Frame ------------------------------------------------------------------- */

main {
  max-width: 1180px;
  margin: 0 auto;
  padding: 44px var(--gutter) 88px;
}

body.doc:not(.has-toc) main {
  max-width: 860px;
}

article {
  min-width: 0;
  max-width: var(--measure);
}

body.home article {
  max-width: 940px;
}

/* Contents ---------------------------------------------------------------- */

.toc {
  font-family: var(--display);
  font-size: 14px;
  line-height: 1.45;
  margin-bottom: 32px;
}

.toc summary {
  cursor: pointer;
  padding: 10px 0;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 12px;
}

.toc ul {
  margin: 0;
  padding: 0 0 8px;
  list-style: none;
}

.toc li {
  margin: 0;
}

.toc a {
  display: block;
  padding: 4px 0 4px 12px;
  border-left: 2px solid var(--hairline);
  color: var(--dim);
  text-decoration: none;
}

.toc a:hover {
  color: var(--base);
  border-left-color: var(--theme);
}

.toc .d3 a {
  padding-left: 26px;
  font-size: 13px;
}

@media (min-width: 900px) {
  body.has-toc main {
    display: grid;
    grid-template-columns: 236px minmax(0, 1fr);
    gap: 60px;
    align-items: start;
  }

  .toc {
    position: sticky;
    top: 28px;
    max-height: calc(100vh - 56px);
    overflow-y: auto;
    overscroll-behavior: contain;
    margin: 0;
  }

  .toc summary {
    display: none;
  }
}

/* Prose ------------------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  font-family: var(--display);
  line-height: 1.16;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(30px, 4.4vw, 40px);
  font-weight: 700;
  margin: 0 0 24px;
}

body.home h1 {
  font-size: clamp(34px, 5.6vw, 54px);
  max-width: 16ch;
  margin-bottom: 18px;
}

h2 {
  font-size: 25px;
  font-weight: 600;
  margin: 52px 0 14px;
  padding-top: 20px;
  border-top: 1px solid var(--hairline);
}

body.home h2 {
  border-top: none;
  padding-top: 0;
  margin-top: 64px;
}

h3 {
  font-size: 19px;
  font-weight: 600;
  margin: 34px 0 10px;
}

h4 {
  font-size: 16px;
  font-weight: 600;
  margin: 26px 0 8px;
  color: var(--dim);
}

p,
ul,
ol {
  margin: 0 0 17px;
}

ul,
ol {
  padding-left: 26px;
}

li {
  margin: 6px 0;
}

li::marker {
  color: var(--theme);
}

a {
  color: var(--theme);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

a:hover {
  text-decoration-thickness: 2px;
}

strong {
  font-weight: 600;
}

hr {
  border: none;
  border-top: 1px solid var(--hairline);
  margin: 40px 0;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: 6px;
  margin: 20px 0;
}

blockquote {
  margin: 20px 0;
  padding: 2px 20px;
  border-left: 3px solid var(--theme);
  color: var(--dim);
}

blockquote strong {
  color: var(--base);
}

/* Code -------------------------------------------------------------------- */

code {
  font-family: var(--mono);
  font-size: 0.86em;
}

:not(pre) > code {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 4px;
  padding: 0.5px 5px;
  word-break: break-word;
}

pre {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 15px 18px;
  margin: 0 0 20px;
  overflow-x: auto;
  line-height: 1.6;
}

pre code {
  font-size: 13.5px;
  background: none;
  border: none;
  padding: 0;
}

/* What a message is drawn in, on a page and in the playground alike: one
 * class per part the parser found, so the two can never disagree. */
.ink-ph {
  background: var(--ink-ph);
  border-radius: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.ink-brace {
  color: var(--ink-brace);
}

.ink-key {
  color: var(--ink-key);
  font-weight: 600;
}

.ink-modifier {
  color: var(--ink-modifier);
}

.ink-option-key {
  color: var(--ink-option-key);
}

.ink-escape {
  color: var(--ink-escape);
  font-weight: 600;
}

/* A backslash that cancels nothing leaves both characters behind, so it is
 * drawn as the text it stays — and marked, because it reads like an escape. */
.ink-escape.ink-inert {
  color: var(--base);
  font-weight: inherit;
  text-decoration: underline dotted var(--ink-escape);
  text-underline-offset: 3px;
}

/* And what the other languages are drawn in, off the same six hues. A name
 * is what something is called where it is introduced — a JSON member, a
 * grammar rule, a function, the command a shell line runs. */
.tok-name {
  color: var(--ink-key);
  font-weight: 600;
}

.tok-string {
  color: var(--ink-option-key);
}

.tok-number {
  color: var(--ink-escape);
}

.tok-word {
  color: var(--ink-modifier);
}

.tok-punct {
  color: var(--dim);
}

.tok-note {
  color: var(--dim);
  font-style: italic;
}

/* The pivot of a worked example: what the line on its left makes of what it
 * was given. It is structure rather than content, so it is drawn quiet. */
.tok-arrow {
  color: var(--dim);
  font-weight: 600;
}

/* Tables ------------------------------------------------------------------ */

.scroll {
  overflow-x: auto;
  margin: 0 0 22px;
  border: 1px solid var(--hairline);
  border-radius: 7px;
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 15.5px;
  line-height: 1.55;
}

th {
  font-family: var(--display);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
  text-align: left;
  padding: 11px 15px;
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}

td {
  padding: 11px 15px;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}

tr:last-child td {
  border-bottom: none;
}

thead tr:not(:has(th:not(:empty))) {
  display: none;
}

/* The landing page -------------------------------------------------------- */

body.home article > p:first-of-type {
  font-size: 20px;
  color: var(--dim);
  max-width: 56ch;
  margin-bottom: 32px;
}

.examples {
  display: grid;
  gap: 14px;
  max-width: 780px;
  margin: 0 0 22px;
}

.examples pre {
  margin: 0;
}

.cards ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 16px;
  margin: 0 0 20px;
  padding: 0;
  list-style: none;
}

.cards li {
  margin: 0;
  padding: 19px 21px;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--dim);
}

.cards li:hover {
  border-color: var(--theme);
}

.cards li p {
  margin: 0;
}

.cards li strong {
  display: block;
  font-family: var(--display);
  font-size: 17px;
  margin-bottom: 6px;
}

.cards li a {
  text-decoration: none;
}

.cards li a:hover {
  text-decoration: underline;
}

body.home article > h2,
body.home article > p,
body.home article > ul,
body.home article > ol,
body.home article > blockquote,
body.home article > .scroll {
  max-width: var(--measure);
}

/* Footer ------------------------------------------------------------------ */

footer {
  max-width: 1180px;
  margin: 0 auto;
  padding: 26px var(--gutter) 56px;
  border-top: 1px solid var(--hairline);
  font-size: 14.5px;
  color: var(--dim);
}

footer p {
  margin: 0 0 10px;
}

footer .status b {
  color: var(--base);
  font-family: var(--display);
}

footer code {
  font-size: 0.9em;
}

.elsewhere {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
  font-family: var(--display);
}

.terms {
  max-width: 62ch;
  font-size: 13.5px;
}

/* Playground -------------------------------------------------------------- */

body.playground article {
  max-width: 1180px;
}

/* The measure is 68 characters of the body face, so it is set on a block of
 * prose rather than on a heading — `ch` on a heading is the display face's
 * character, which is a different length. */
body.playground article > p,
body.playground .notes {
  max-width: var(--measure);
}

.cases {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 24px;
}

.cases button {
  font-family: var(--display);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--dim);
  background: none;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 5px 15px;
  cursor: pointer;
}

.cases button:hover {
  color: var(--base);
  border-color: var(--base);
}

/* The case the page is holding. */
.cases button[aria-pressed="true"],
.cases button[aria-pressed="true"]:hover {
  color: var(--theme);
  border-color: var(--theme);
}

/* The call on the left, what it answered with on the right, and one column
 * under the width where two would each be too narrow to read a message in. */
.play {
  display: grid;
  gap: 30px;
  align-items: start;
  margin-bottom: 8px;
}

@media (min-width: 900px) {
  .play {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 40px;
  }
}

.inputs {
  display: grid;
  gap: 18px;
  min-width: 0;
}

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

.label,
.field label,
.result h2,
.props summary {
  font-family: var(--display);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

.field .hint {
  font-weight: 500;
  letter-spacing: 0.04em;
}

.hint.under {
  margin: 0;
  font-family: var(--body);
  font-size: 14.5px;
  line-height: 1.5;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--dim);
}

.inputs textarea,
.inputs select {
  width: 100%;
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--base);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 11px 13px;
  resize: vertical;
}

/* A locale is a handful of characters, so the control is as wide as what it
 * holds rather than as wide as the column it sits in. */
.inputs select {
  width: auto;
  justify-self: start;
  min-width: 20ch;
  resize: none;
}

/* The coloured message ---------------------------------------------------- */

/* The control and the block that draws it occupy one cell, so they sit
 * exactly on top of one another and the row grows to whichever is taller.
 * The control is second, so it paints over the block. */
.editor {
  display: grid;
  min-width: 0;
}

.editor > * {
  grid-area: 1 / 1;
}

/* Every metric that decides where a line breaks, stated once for both boxes:
 * they agree on the wrapping only while they agree on all of it. */
.ink,
.inputs .editor textarea {
  font-family: var(--mono);
  font-size: 13.5px;
  line-height: 1.6;
  padding: 11px 13px;
  border: 1px solid transparent;
  border-radius: 7px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: normal;
}

.ink {
  margin: 0;
  overflow: hidden;
  background: var(--surface);
  border-color: var(--hairline);
  color: var(--base);
  pointer-events: none;
}

/* The control keeps the caret, the selection and the keyboard; the colour
 * under it is what is read. It never scrolls on its own — the cell is as tall
 * as the drawn message — so the two can never slip apart. */
.inputs .editor textarea {
  background: none;
  color: transparent;
  caret-color: var(--base);
  overflow: hidden;
  resize: none;
}

/* A selection paints over the block, so it is drawn as a wash rather than a
 * fill: opaque, it would hide the very colouring it is selecting. */
.editor textarea::selection {
  background: color-mix(in srgb, var(--theme) 26%, transparent);
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--display);
  font-size: 12px;
  line-height: 1.5;
  color: var(--dim);
}

.legend span {
  font-family: var(--mono);
  font-size: 11.5px;
}

.props summary {
  cursor: pointer;
}

.props > p {
  font-size: 15px;
  line-height: 1.6;
  color: var(--dim);
  margin: 10px 0 12px;
}

.results {
  display: grid;
  gap: 26px;
  min-width: 0;
}

.result h2 {
  border-top: none;
  padding-top: 0;
  margin: 0 0 9px;
}

#output {
  display: block;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  padding: 16px 18px;
  font-size: 18px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

#output.empty {
  color: var(--dim);
  font-style: italic;
}

/* A report is the parser saying something went wrong, so it takes the rail the
 * theme colour is spent on; a parameter is an observation and takes a plain
 * one. */
.report,
.param {
  border-left: 2px solid var(--hairline);
  padding-left: 15px;
  margin-bottom: 16px;
}

.report {
  border-left-color: var(--theme);
}

.report:last-child,
.param:last-child {
  margin-bottom: 0;
}

.report .head,
.param .head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 11px;
  margin: 0 0 5px;
}

.origin,
.kind {
  font-family: var(--display);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

.said {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.55;
}

.at,
.values {
  margin: 5px 0 0;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--dim);
}

.excerpt {
  margin: 10px 0 0;
  padding: 9px 12px;
  font-size: 12.5px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.quiet {
  margin: 0;
  font-size: 15.5px;
  color: var(--dim);
}

.bad {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.5;
  border-left: 2px solid var(--theme);
  padding-left: 11px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
