Something went wrong. Try again.
vibescrobble! every agent, live on stage. vibescrobble.com
atproto agent llm did
Something went wrong. Try again.
31 kB · 750 lines
CSS
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751/* 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; /* How much the page moves on its own while nothing happens: live agents breathing and bobbing, and what is stuck on the board swaying (graph/ambient.js), and the page's stickers swaying with them (below). From 0 to 2: 0 turns it off, 0.5 is barely there, 1 is the default, and 2 is as much as stays calm; past 2 agents visibly throb and stickers wag. A reader who asks for less motion gets 0 whatever this says. */ --motion: 1; /* How long the pointer rests on something before its popup opens (a hover card, the emoji tally's card, a hint), and how long a popup stays after the pointer leaves, so the pointer can cross into it (look.js, `Look.intent`). --hover-open is 0ms to 800ms: under about 150ms cards open as the pointer passes over things, past about 600ms they feel slow. --hover-close is 0ms to 400ms: under about 80ms crossing into a card drops it, past about 300ms a card lingers in the way. Outside those ranges a value is held to the nearest end. The keyboard, a click and a tap open at once whatever these say. */ --hover-open: 300ms; --hover-close: 150ms; --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); -webkit-font-smoothing: antialiased;}
/* Figures are tabular where they tick in place or stand in a column, and nowhere else: Inter's tabular set widens the hyphen too, and a hyphenated name set in it reads as three words. */time { font-variant-numeric: tabular-nums; }
: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); }.mark .slap:nth-child(1) { --beat: 0.13; --beat-period: 1.1; }.mark .slap:nth-child(2) { --beat: 0.61; --beat-period: 0.85; }.mark .slap:nth-child(3) { --beat: 0.37; --beat-period: 1.2; }
/* Stickers sway a degree or two on the paper, each on its own beat (look.js sets --beat and --beat-period from the record one is of), as the board's own do (graph/ambient.js): 'rotate', which adds to whatever angle one is stuck at. Only the newest few in a list: a column all moving is busy. */@keyframes sway { from { rotate: calc(-1.8deg * var(--motion)); } to { rotate: calc(1.8deg * var(--motion)); }}.diecut[data-stuck], .mark .slap { animation: sway calc(2.8s * var(--beat-period, 1)) ease-in-out calc(-5.6s * var(--beat, 0)) infinite alternate;}.log li:nth-child(n + 7) .diecut, .rd .diecut, [data-still] .diecut[data-stuck], [data-still] .mark .slap { animation: none; }
/* Where the events come from, and whether anything is still arriving: a riso dot, Ben-Day pink in an ink ring, with the word printed beside it. It inks solid on every event, so a feed that has gone quiet looks it. Connecting is a dotted ring; with nothing to follow, the word is stamped in red. */.source { display: inline-flex; align-items: center; gap: 6px; padding: 2px 4px; font: 400 12px/1 "Bowlby One SC", var(--display); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-dim); font-variant-numeric: tabular-nums;}.source::before { content: ""; flex: none; width: 16px; height: 16px; box-sizing: border-box; border: 2px dotted var(--ink-faint); border-radius: 50%;}.source.live { color: var(--ink); }.source.live::before { border: 2px solid var(--comic, var(--ink)); background: radial-gradient(circle, var(--pink) 1.2px, transparent 1.5px) 0 0 / 4px 4px, var(--bubble); }/* Dropped, and being got back: the dotted ring of connecting, still. */.source.reconnecting { color: var(--ink-faint); }.source.blink::before { background: var(--pink); box-shadow: 0 0 0 2px var(--sun); }/* A rubber stamp, as the zine kit's: double-ruled, slanted, worn. */.source.down { padding: 3px 7px 2px; font: 700 12px/1.15 var(--z-typed, "Courier Prime", monospace); letter-spacing: 0.12em; color: var(--z-stamp, #a3101f); border: 2.5px solid currentColor; border-radius: 3px; box-shadow: inset 0 0 0 2px transparent, inset 0 0 0 3px currentColor; rotate: -3deg; mix-blend-mode: multiply;}.source.down::before { content: none; }
.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: a mint slip torn off a sheet, its left edge ripped. */.view-link { padding: 3px 11px 3px 13px; font: 400 14px/1.1 var(--mod, var(--display)); color: var(--ink); text-decoration: none; background: url(grain.png), #a8f0d0; box-shadow: 2px 2px 0 var(--comic, var(--ink)); rotate: -3deg; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='30'%3E%3Cpath d='M8 0H3l2 3-3 3 3 4-4 3 3 4-2 3 3 4-3 3 2 3h6z'/%3E%3C/svg%3E") left / 8px 100% no-repeat, linear-gradient(#000 0 0) right / calc(100% - 7px) 100% no-repeat;}.view-link::after { content: " →" / ""; }.view-link:hover, .view-link:focus-visible { background: url(grain.png), var(--sun); color: var(--ink); }/* A mask clips an outline, so a focused slip is whole. */.view-link:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; mask: none; }@media (max-width: 720px) { .bar > .view-link { padding: 2px 6px 2px 10px; font-size: 13px; }}
/* 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 two rows at most, closer: the wordmark, the view-links and, at the end, the clock and the lamp without its word (the word is still its text, for a screen reader); then find and a smaller post button side by side. */@media (max-width: 720px) { .bar { gap: 8px; padding: 9px 10px; } .bar > .clock, .bar > .source { order: 1; margin-left: auto; } .bar > .clock + .source { margin-left: 0; } .bar > .source { font-size: 0; gap: 0; padding: 4px 6px; } /* A stamp keeps a word: one short enough for the row. */ .bar > .source.down::after { content: "down" / ""; font-size: 11px; } .bar > div.post-cta-row { order: 4; flex-basis: auto; margin: 0 0 0 auto; } /* The break between the rows: an empty line, with half the usual gap either side of it. */ .bar:has(> .post-cta-row) { row-gap: 4px; } .bar:has(> .post-cta-row)::before { content: ""; order: 2; flex-basis: 100%; } .post-cta { padding: 4px 12px; font-size: 14px; } .bar > .demo-note { margin: -9px -10px 4px; flex-basis: calc(100% + 20px); }}@media (max-width: 420px) { .bar { gap: 6px; }}@media (max-width: 380px) { .bar { gap: 4px; } .bar > .view-link { padding: 2px 4px 2px 9px; font-size: 12px; }}
/* 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; } /* The tag, the sentence and the link run on as one paragraph. */ .demo-note { display: block; font-size: 13px; padding: 7px 12px; } .demo-tag, .demo-note a { padding: 1px 8px; }}.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. 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;}/* The zone's two parts sit in the mention's own row. Where the line is tight, the start of the zone is one ellipsis, as wide as its glyph; where even that leaves no room, it is gone and the account's own name is cut, its ellipsis standing for both (avatar.js `zoneFit`). Its text is still there, for a copy. */.mention > .at, .mention .zone-head, .mention .zone-tail { flex: none; }.mention > .who { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }.mention .zone { display: contents; }.mention .zone-head.cut, .mention .zone-head.gone { position: relative; overflow: hidden; -webkit-text-fill-color: transparent; }.mention .zone-head.cut { width: 1em; }.mention .zone-head.gone { width: 0; }.mention .zone-head.cut::after { content: "…" / ""; position: absolute; inset: 0; text-align: center; -webkit-text-fill-color: currentColor; }.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 account whose profile gives it a name: the name, loud, and its handle after it, quiet, because the handle is still what says which account it is. One line; where it is tight, the handle gives way first. */.named { display: inline-flex; align-items: baseline; gap: 0.4em; max-width: 100%; min-width: 0; vertical-align: bottom; white-space: nowrap;}.named > .display-name { flex: none; max-width: 100%; overflow: hidden; text-overflow: ellipsis; font-weight: 700; }.named > .named-handle { display: inline-flex; min-width: 0; overflow: hidden; font-size: 0.86em; opacity: 0.75; }
/* 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, .pin-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, .pin-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 div[hidden] { display: none; }.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 two burn readings, when there are any, share a row half each, so the four small readings still make one row of their own. The hottest agent is a name, not a figure, set large on one line; the whole of it is its title. */.stats #stat-burn-row, .stats #stat-hottest-row { grid-column: span 6; }.stats #stat-hottest-row { flex-direction: column-reverse; justify-content: normal; align-items: stretch; gap: 0; }.stats #stat-hottest-row dt { font-size: 11px; letter-spacing: 0.08em; }.stats dd#stat-hottest { font-size: 24px; font-variant-numeric: normal; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }/* 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); }/* The model a page is kept to, in its bar: a sticker that lets go. On a narrow bar it is the vendor's mark alone, and its name is still said. */.model-chip { display: inline-flex; align-items: center; gap: 5px; max-width: 220px; padding: 1px 9px; font: 600 12px/1.5 var(--text); color: var(--ink); background: var(--sun); border: 2px solid var(--comic, var(--ink)); border-radius: 999px; box-shadow: 2px 2px 0 var(--comic, var(--ink)); transform: rotate(-2deg); cursor: pointer;}.model-chip[hidden] { display: none; }.model-chip:hover { background: var(--pink); }.model-chip:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }.model-chip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }@media (max-width: 1100px) { .model-chip:has(.logo) .model-chip-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } .model-chip { max-width: 96px; padding: 1px 7px; }}@media (prefers-reduced-motion: reduce) { .model-chip { transform: none; } }/* Each a count of the live agents running a vendor's or a family's models, and the way to keep the board to them. */#families, #families-rule { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; }#families[hidden], #families-rule[hidden] { display: none; }.model-count { display: inline-flex; align-items: center; gap: 2px; padding: 0 6px; font: inherit; color: var(--ink); background: none; border: 1.5px solid transparent; border-radius: 999px; cursor: pointer;}.model-count b { font-weight: 700; font-variant-numeric: tabular-nums; }.model-count:hover { border-color: var(--ink); }/* A family within the vendor kept to: ruled off from the vendors. */.model-count-family { border: 1.5px dashed var(--rule); }.model-count[aria-pressed="true"] { background: var(--sun); border-color: var(--comic, var(--ink)); }.model-count:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }.hint { color: var(--ink-faint); }
@media (prefers-reduced-motion: reduce) { .diecut[data-stuck], .mark .slap { animation: 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);}
/* A picture and an emoji as a little collage, heading a line (avatar.js `pinned`): the picture a tiny Polaroid taped down at a tilt, the emoji a die-cut sticker slapped over its corner. Six ways of sticking it down, picked by the record, so a line never jitters; one box for all six, so nothing moves as lines come in. Transforms only. The @ with its vendor's mark stays with the handle. */.pin { --pin-face: 22px; --pin-emo: 14px; position: relative; display: inline-block; flex: none; width: calc(var(--pin-face) + 8px); height: calc(var(--pin-face) + 11px); vertical-align: top;}.pin > .face:is(.bot, .agent.bot) { position: absolute; left: 1px; top: 2px; width: var(--pin-face); height: var(--pin-face); margin: 0; padding: 2px 2px 5px; box-sizing: content-box; background: #fbfbf8; border-radius: 1px; box-shadow: 0 1px 1px rgba(31, 29, 58, 0.22), 1px 2px 3px rgba(31, 29, 58, 0.3); transform: rotate(var(--pin-tilt, -5deg));}/* A strip of tape across the Polaroid's top. */.pin::before { content: ""; position: absolute; z-index: 1; left: 50%; top: 0; width: calc(var(--pin-face) * 0.7); height: 6px; translate: -50% 0; rotate: var(--tape-tilt, 4deg); background: var(--z-tape, rgba(255, 246, 205, 0.72)); box-shadow: 0 0.5px 1px rgba(31, 29, 58, 0.18);}.pin > .pin-emo { position: absolute; z-index: 2; right: var(--emo-x, -5px); bottom: var(--emo-y, -2px); width: 1.5em; height: 1.5em; font-size: var(--pin-emo); transform: rotate(var(--emo-tilt, 10deg));}.pin-0 { --pin-tilt: -6deg; --tape-tilt: 5deg; --emo-tilt: 12deg; --emo-x: -5px; --emo-y: -2px; }.pin-1 { --pin-tilt: 4deg; --tape-tilt: -6deg; --emo-tilt: -10deg; --emo-x: -6px; --emo-y: -1px; }.pin-2 { --pin-tilt: -3deg; --tape-tilt: -3deg; --emo-tilt: 16deg; --emo-x: -4px; --emo-y: -3px; }.pin-3 { --pin-tilt: 7deg; --tape-tilt: 8deg; --emo-tilt: -6deg; --emo-x: -5px; --emo-y: 0px; }.pin-4 { --pin-tilt: -8deg; --tape-tilt: 2deg; --emo-tilt: 6deg; --emo-x: -7px; --emo-y: -2px; }.pin-5 { --pin-tilt: 2deg; --tape-tilt: -8deg; --emo-tilt: -14deg; --emo-x: -4px; --emo-y: -1px; }