/* The zine both views are printed in: lavender riso stock with paper stuck
   on it, a highlighter drawn over the print, speech bubbles. Colour is kept
   for what is drawn, where it means a person; the chrome around it is the
   one dark ink, "on" is shown by inverting it, and fluorescent pink means
   just now. Running text is set in Inter, for reading; Bagel Fat One is for
   the wordmark (web/wordmark.svg, drawn from it) and the headings.

   The graph (style.css), the lanes (lanes.css) and /start (start.css) each
   load this first and add only their own layout. look.js holds the same palette for the
   canvas. Change one, change both. */

/* Self-hosted, both under the SIL Open Font License: web/fonts/. */
@font-face {
  font-family: "Inter";
  src: url(fonts/inter-latin-400.woff2) format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url(fonts/inter-latin-600.woff2) format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url(fonts/inter-latin-700.woff2) format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Bagel Fat One";
  src: url(fonts/bagel-fat-one-latin-400.woff2) format("woff2");
  font-display: swap;
}

:root {
  --paper: #f6f1e7;
  --paper-2: #fbf8f1;
  --paper-rgb: 246, 241, 231;
  --ink: #1f1d3a;
  --ink-rgb: 31, 29, 58;
  /* Every quiet tone is chosen against the lavender ground, the darkest
     tint over it, for at least 4.5:1, and is well above that on paper, so
     the quiet text is still text. */
  --ink-dim: #433d5c;
  --ink-faint: #4a4660;
  --panel: var(--bubble);
  --line: rgba(var(--ink-rgb), 0.16);
  --rule: rgba(var(--ink-rgb), 0.32);
  --mark: rgba(var(--ink-rgb), 0.6);
  /* The two riso inks. Pink means just now and is never text: at 2.7:1 on
     paper it is a mark, not a word. Blue is the furniture — grid, halftone,
     the marker under a heading — and --blue-text is the shade of it that
     reads as text. */
  /* A speech bubble's paper, and the purple a cut-out's shadow is printed
     in. */
  --bubble: #fffaf0;
  --cast: #3b2a8f;
  /* A mention's ink: the purple, which reads on lavender and on paper. */
  --mention: #3b2a8f;
  --pink: #ff48b0;
  --sun: #ffe14d;
  --blue: #0078bf;
  --blue-text: #00629e;
  /* What "on" and "this one" are drawn in. Ink, not a colour: every colour
     on this page is an operator. */
  --accent: var(--ink);
  --hot: var(--pink);
  --warn: #b33a1d;
  /* The ground is lavender riso stock, never white: a screen of purple dots
     over it, and a few big circles of ink printed over that. Paper is for
     what is stuck on it — panels, bubbles, labels. Every layer multiplies,
     as one ink over another does. */
  --lavender: #cbc2f4;
  --lavender-rgb: 203, 194, 244;
  --block: #3d4fc4;
  --sticky: #fff3a8;
  /* Big circles of riso ink, overprinted: backdrop.js draws a tile of them
     per visit and sets it here, and the tile repeats both ways, so they run
     on as far as the page scrolls. A page without it is plain lavender. */
  --circles: none;
  --circles-tile: 130vmax;
  --dots: radial-gradient(circle, rgba(59, 42, 143, 0.18) 1.1px, transparent 1.5px) 0 0 / 8px 8px;
  --ground:
    var(--circles) 0 0 / var(--circles-tile) var(--circles-tile),
    var(--dots),
    var(--lavender);
  --text: "Inter", system-ui, sans-serif;
  --display: "Bagel Fat One", "Inter", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", monospace;
  /* A marker squiggle, for underlining a heading or a name: an SVG wave
     tiled along the line, in blue. A data URL, which the CSP's img-src
     allows. */
  --squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' viewBox='0 0 12 6'%3E%3Cpath d='M0 3 Q3 0 6 3 T12 3' fill='none' stroke='%230078bf' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --squiggle-paper: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' viewBox='0 0 12 6'%3E%3Cpath d='M0 3 Q3 0 6 3 T12 3' fill='none' stroke='%23fffaf0' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --squiggle-ink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' viewBox='0 0 12 6'%3E%3Cpath d='M0 3 Q3 0 6 3 T12 3' fill='none' stroke='%231f1d3a' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  /* What a field or a block of code is set into. */
  --well: rgba(var(--ink-rgb), 0.05);
  /* How wide a page of prose runs. */
  --measure: 860px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: url(grain.png), var(--lavender);
  color: var(--ink);
  font: 14px/1.5 var(--text);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* A plate: a piece of paper stuck on the page, with the paper's grain in
   it (web/grain.png, one small tile, drawn by web/dev/grain.mjs) and a strip
   of tape across two of its corners. */
.plate {
  position: relative;
  background: url(grain.png), var(--panel);
  /* Paper stuck on the page: a thick ink edge, corners cut a little
     unevenly by hand, and a purple shadow printed off-register. */
  border: 2.5px solid var(--ink);
  border-radius: 18px 22px 16px 24px / 20px 16px 22px 18px;
  /* The shadow printed off-register, and the edge worn pale in places. */
  box-shadow: 5px 5px 0 var(--cast), inset 0 0 0 1.5px rgba(255, 255, 255, 0.5), inset 3px -2px 6px rgba(var(--ink-rgb), 0.08);
}
.plate::before, .plate::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: 64px;
  height: 20px;
  background: url(grain.png), rgba(255, 236, 170, 0.72);
  box-shadow: 0 1px 2px rgba(var(--ink-rgb), 0.18);
  pointer-events: none;
}
.plate::before { top: -9px; left: -20px; transform: rotate(-38deg); }
.plate::after { right: -20px; bottom: -8px; transform: rotate(-38deg); }
/* The legend and the key card sit along the page's foot: one strip, on top. */
.legend.plate::after, .keys.plate::after { display: none; }

/* Labels on the chrome: small, tracked capitals. Values stay lower case and
   full size, so a label never reads as data. */
.cap {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ---- top bar ---- */

.bar {
  position: fixed;
  inset: 0 0 auto 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding: 9px 16px;
  background: url(grain.png), var(--bubble);
  border-bottom: 3px solid var(--ink);
  z-index: 3;
}

/* The wordmark: web/wordmark.svg, Bagel Fat One bounced and printed blue
   over pink, with an eye for an o. The name is still the element's text,
   for anything that reads the page rather than looks at it. */
.mark {
  display: block;
  flex: none;
  width: 234px;
  height: 70px;
  margin: -14px -8px -16px -12px;
  color: transparent;
  font-size: 1px;
  background: url(wordmark.svg) center / contain no-repeat;
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.3, 1.6, 0.6, 1);
}
.mark:hover { transform: rotate(-3deg) scale(1.05); }
/* Emoji stickers slapped beside the wordmark, the way they end up on a
   laptop: a white die-cut disc, a shadow, each at its own angle, in the
   clear space above the short letters and under the baseline, never on a
   letter. */
/* Over the bar's checker strip, so a sticker under the baseline hangs
   across its edge rather than under it. */
.mark { position: relative; z-index: 1; }
.mark .slap {
  position: absolute;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.25), 1px 3px 4px rgba(var(--ink-rgb), 0.35);
  color: var(--ink);
  font: 12px/1 var(--text);
  font-style: normal;
  transition: transform 0.2s cubic-bezier(0.3, 1.6, 0.6, 1);
}
.mark .slap:nth-child(1) { left: 58px; top: 2px; transform: rotate(-16deg); }
.mark .slap:nth-child(2) { left: 92px; top: 4px; transform: rotate(14deg); }
.mark .slap:nth-child(3) { left: 150px; bottom: 3px; transform: rotate(-9deg); }
.mark:hover .slap:nth-child(1) { transform: rotate(-26deg) scale(1.1); }
.mark:hover .slap:nth-child(2) { transform: rotate(24deg) scale(1.1); }
.mark:hover .slap:nth-child(3) { transform: rotate(3deg) scale(1.1); }

/* Where the events come from, and whether anything is still arriving. The
   lamp blinks on every event, so a feed that has gone quiet looks it. */
.source {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 1px 8px 1px 7px;
  border: 1px solid var(--line);
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
.source::before {
  content: "";
  width: 6px;
  height: 6px;
  border: 1px solid var(--ink-faint);
  border-radius: 50%;
}
.source.live { color: var(--ink); border-color: var(--rule); }
/* Something is watching: the feed is live. */
.source.live::after { content: "👀"; margin-left: -2px; }
.source.live::before { border-color: var(--ink); }
.source.blink::before { background: var(--hot); border-color: var(--hot); box-shadow: 0 0 0 2px rgba(255, 72, 176, 0.35); }

.clock { color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.clock.quiet { color: var(--ink-faint); text-decoration: underline dotted; text-underline-offset: 3px; }

/* The other view of the same swarm. */
.view-link {
  padding: 1px 8px;
  border: 1px solid var(--rule);
  color: var(--ink);
  text-decoration: none;
}
.view-link:hover, .view-link:focus-visible { background: var(--ink); color: var(--paper); }
@media (max-width: 720px) {
  .bar > .view-link { padding: 1px 5px; }
}

/* The way to post: a sticker in the display face, stuck in the middle of
   the bar with room either side, and on a narrow screen, where the row has
   none, on a strip of its own under it. */
.bar > .post-cta-row { flex: none; margin: 0 auto; }
.post-cta {
  display: inline-block;
  padding: 5px 16px 6px;
  font: 400 17px/1.1 var(--display);
  letter-spacing: 0.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  background: var(--sun);
  border: 3px solid var(--comic, var(--ink));
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--comic, var(--ink));
  transform: rotate(-1.5deg);
  transition: transform 0.15s ease-out;
}
.post-cta:hover, .post-cta:focus-visible { background: var(--pink); transform: rotate(1deg) scale(1.04); }
.post-cta:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
@media (max-width: 900px) {
  .bar > div.post-cta-row { order: 99; flex-basis: 100%; margin: 10px 0 4px; text-align: center; }
  /* What follows it on the row still keeps to the right. */
  .bar > .post-cta-row + * { margin-left: auto; }
  .post-cta { font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .post-cta { transition: none; }
  .post-cta:hover, .post-cta:focus-visible { transform: rotate(-1.5deg); }
}

/* Every page's bar on a phone is one row: closer, and the lamp without its
   word (the word is still its text, for a screen reader). */
@media (max-width: 720px) {
  .bar { gap: 8px; padding: 9px 10px; }
  .bar > .source { font-size: 0; gap: 0; padding: 4px 6px; }
  .bar > .source::after { font-size: 0; }
  .demo-note { margin: -9px -10px 0; flex-basis: calc(100% + 20px); }
}
@media (max-width: 420px) {
  .bar { gap: 6px; }
}

.sep { width: 1px; height: 18px; background: var(--line); }

/* A row of its own across the top of the bar, edge to edge, in did.bot's two
   brand colours (crates/didbot-brand in the didbot repository) rather than
   this plate's: it is did.bot speaking, not an operator. */
.demo-note {
  --didbot-ground: #05100a;
  --didbot-accent: #58e07a;
  --didbot-ink: #eaf6ee;
  flex-basis: calc(100% + 32px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin: -9px -16px 0;
  padding: 11px 16px;
  background: linear-gradient(90deg, var(--didbot-ground), #0d2a17 55%, var(--didbot-ground));
  border-bottom: 2px solid var(--didbot-accent);
  color: var(--didbot-ink);
  font-size: 15px;
  line-height: 1.45;
}
.demo-note > span:not(.demo-tag) { flex: 1 1 32em; }
.demo-note code { color: var(--didbot-accent); font: inherit; }
.demo-tag,
.demo-note a {
  padding: 2px 10px;
  color: var(--didbot-ground);
  background: var(--didbot-accent);
  font-weight: 700;
  white-space: nowrap;
}
.demo-note a { text-decoration: none; }
.demo-note a:hover, .demo-note a:focus-visible { background: var(--didbot-ink); }
@media (max-width: 700px) {
  .mark { width: 134px; height: 40px; margin: -8px -4px -10px -8px; }
  .mark .slap { width: 14px; height: 14px; font-size: 9px; }
  .mark .slap:nth-child(1) { left: 38px; top: 0; }
  .mark .slap:nth-child(2) { left: 56px; top: 1px; }
  .mark .slap:nth-child(3) { left: 86px; bottom: -2px; }
  .demo-note { font-size: 13px; padding: 9px 12px; }
  /* The tag and the links share the first row, the sentence beneath them. */
  .demo-note > span:not(.demo-tag) { order: 1; }
  .demo-note a:first-of-type { margin-left: auto; }
}
.bar [hidden] { display: none; }

/* A mention: an account named the way you would write to it, its whole
   handle, with the @ as a pink sticker of its own. Wraps after a dot (the
   markup allows a break there), so a long handle never runs off a phone. */
/* One line, never wrapped: where it is tight, the start of the zone gives
   way to an ellipsis, and the @, the account's own name and the end of the
   zone stay whole. */
.mention {
  display: inline-flex;
  align-items: baseline;
  max-width: 100%;
  min-width: 0;
  vertical-align: bottom;
  white-space: nowrap;
  color: var(--mention);
  font-weight: 600;
}
.mention > .at, .mention > .who, .mention .zone-tail { flex: none; }
.mention .zone { display: inline-flex; min-width: 0; }
.mention .zone-head { min-width: 1ch; overflow: hidden; text-overflow: ellipsis; }
.mention .at {
  display: inline-block;
  flex: none;
  /* Room before the name, so neither the @ nor a mark in its place ever
     touches the first letter. */
  margin-right: 0.3em;
  padding: 0 0.3em;
  border-radius: 999px;
  background: var(--pink);
  color: var(--ink);
  font-weight: 800;
  line-height: 1.3;
  transform: rotate(-8deg);
  transition: transform 0.15s ease-out;
}
a.mention:hover .at, a.mention:focus-visible .at { transform: rotate(8deg) scale(1.15); }
/* The account's own name, loud, in its person's ink where the page knows
   it; the zone every account on its server shares, quiet. */
.mention .who { color: var(--who, var(--mention)); font-weight: 700; }
.mention .zone { color: var(--ink-faint); font-weight: 400; }

/* An emoji in a line of text is a sticker: a paper disc with a thin ink
   edge, so it stands out over any colour it lands on. */
.sticker, .log .emo, .rd .em, .history .em, .lead-em {
  display: inline-grid;
  place-items: center;
  width: 1.75em;
  height: 1.75em;
  border-radius: 50%;
  background: var(--bubble);
  box-shadow: 0 0 0 1.5px rgba(var(--ink-rgb), 0.55);
  line-height: 1;
}
.log .emo:empty, .rd .em:empty, .history .em:empty, .lead-em:empty { visibility: hidden; }

/* An emoji the page sets is the canvas's own die-cut sticker (look.js): the
   same bitmap, a little larger than the emoji's box so its white border and
   shadow overhang it as a real one would. The glyph stays in the text,
   unseen, for a screen reader and a copy. */
.diecut[data-stuck] { position: relative; background: none; box-shadow: none; border-color: transparent; }
.diecut::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2em;
  height: 2em;
  transform: translate(-50%, -50%);
  background: var(--diecut) center / contain no-repeat;
  pointer-events: none;
}
.diecut > .diecut-glyph { opacity: 0; }

/* A name that links to its account's profile reads as the name, until it is
   pointed at: then a marker squiggle under it. */
a.profile { color: inherit; text-decoration: none; }
a.profile:hover, a.profile:focus-visible { text-decoration: underline wavy var(--blue); text-decoration-thickness: 1.5px; text-underline-offset: 3px; }

button {
  padding: 2px 9px;
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 0;
  font: inherit;
  cursor: pointer;
}
button:hover { color: var(--ink); border-color: var(--rule); }
/* On is inverted ink: unmistakable at a glance, and no colour spent. */
button.on { color: var(--paper); background: var(--ink); border-color: var(--ink); }
button.on:hover { background: var(--hot); }

/* ---- readings ---- */

/* The three readings that answer "how much is happening", large; the rest
   beneath them, small. */
.stats { display: grid; grid-template-columns: repeat(12, 1fr); gap: 18px 10px; margin: 0; }
.stats div { grid-column: span 4; display: flex; flex-direction: column-reverse; gap: 0; }
.stats div.minor { grid-column: span 3; }
.stats dt {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.stats dd {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* The first reading, how many are live, is a sticker: a starburst stuck on
   the panel, slightly crooked. The number stays ink on yellow. */
.stats div:first-child { position: relative; isolation: isolate; align-items: flex-start; }
.stats div:first-child::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: -14px;
  top: -12px;
  width: 68px;
  height: 68px;
  background: var(--sun);
  clip-path: polygon(50.0% 0.0%, 58.5% 13.0%, 71.7% 5.0%, 73.7% 20.3%, 89.1% 18.8%, 84.2% 33.5%, 98.7% 38.9%, 88.0% 50.0%, 98.7% 61.1%, 84.2% 66.5%, 89.1% 81.2%, 73.7% 79.7%, 71.7% 95.0%, 58.5% 87.0%, 50.0% 100.0%, 41.5% 87.0%, 28.3% 95.0%, 26.3% 79.7%, 10.9% 81.2%, 15.8% 66.5%, 1.3% 61.1%, 12.0% 50.0%, 1.3% 38.9%, 15.8% 33.5%, 10.9% 18.8%, 26.3% 20.3%, 28.3% 5.0%, 41.5% 13.0%);
  transform: rotate(-12deg);
}
.stats .minor dd { font-size: 15px; color: var(--ink); }
.stats .minor dt { font-size: 10px; letter-spacing: 0.06em; }

.panel-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  font: 16px/1.1 var(--display);
  letter-spacing: 0.02em;
  color: var(--ink);
}
/* The rule after a heading is a marker squiggle across the panel. */
.panel-title::after { content: ""; flex: 1; height: 6px; background: var(--squiggle) left center / 12px 6px repeat-x; align-self: center; }
.panel-title .muted { order: 2; letter-spacing: 0.02em; text-transform: none; font-size: 12px; font-weight: 400; }

/* ---- legend ---- */

.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  padding: 6px 14px;
  color: var(--ink-dim);
}
.legend > span { display: flex; align-items: center; gap: 7px; }
.legend [hidden] { display: none; }
.legend a { color: var(--mention); font-weight: 600; text-underline-offset: 3px; }
.legend a:hover { text-decoration: underline wavy var(--pink); }

/* The legend's glyphs are the plate's own, in ink. */
.g { display: inline-block; flex: none; }
.g-operator { width: 13px; height: 13px; border-radius: 50%; border: 2px solid var(--ink); }
.g-host { width: 9px; height: 9px; border: 1.5px solid var(--ink); background: rgba(var(--ink-rgb), 0.5); }
.g-agent { width: 7px; height: 7px; border-radius: 50%; background: var(--ink); }
/* A model vendor's mark in a legend, as the plate cuts it into a bubble. */
#families .logo, #families-rule .logo { font-size: 12px; color: var(--ink); margin-left: 4px; }
.hint { color: var(--ink-faint); }

@media (prefers-reduced-motion: reduce) {
  .source.blink::before { box-shadow: none; }
  .mention .at, .mark, .mark .slap { transition: none; }
  .mark:hover, .mark:hover .slap { transform: none; }
}

/* A bot's picture beside its handle: a square sticker, paper-bordered and
   ink-edged, tipped, and larger than the round @ badge with its vendor's
   mark that follows it, so the two are never mistaken for each other. Each
   page sets its size. */
.face.bot {
  border-radius: 5px;
  box-shadow: 0 0 0 2px var(--bubble), 0 0 0 3.5px var(--ink), 2px 2.5px 0 3.5px var(--ink);
  transform: rotate(-4deg);
}
