@import url("/v2/ui.css");
/* EarthOnline Chat. One theme: pure black, after apple.com's dark pages. The same base as EarthOnline Games.
   Colour comes from the robots only (src/robot.js); the interface is black, white and grey.
   One thing moves: an online robot blinks now and then. Nothing else does: no entrance animation, no transition,
   hover changes one step of background, a state that refreshes is swapped in place.
   Control shapes and sizes are Apple's, measured on apple.com (2026-10-01): buttons are capsules (44 / 36 / 24px tall),
   fields are 56px tall with a 12px radius. The login page alone is measured from another page; its part is at the end.
   The picture on the login page is the one exception to the two rules above (Bonan, 2026-10-02): its floor has colour
   of its own that flashes on the beat, lit by beams that slowly change colour, its robots drift as well as blink, and
   confetti in the robots' colours falls through it. Nowhere else. */
/* 10-02: every token, the type sizes and every control (buttons, fields, toggles, badges, the confirm box) live in
   public/v2/ui.css, loaded first (design/05-ui-system.md). This file lays pages out. */
:root {
  --top-h: 52px;
  --top-glass-line: rgb(255 255 255 / .1);
  --top-glass-bg: rgb(0 0 0 / .72);
  --top-glass-filter: saturate(180%) blur(20px);
  /* the login page's own sizes (the Cube login page's): title, sentence under it, words on the picture, the "check your email" title; its two radii */
  --t-login-title: 24px; --t-login-text: 16px; --t-login-words: 28px; --t-login-head: 18px; --r-login: 8px; --r-login-pane: 26px;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; background: var(--bg); }
body { margin: 0; background: var(--bg); color: var(--fg); font: var(--text-base)/var(--lh-base) var(--font); -webkit-font-smoothing: antialiased; }
a { color: var(--fg); text-decoration-color: var(--muted); text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }
a, button, input, textarea, summary { touch-action: manipulation; }
/* one focus ring for links and the rest, keyboard only (controls draw shadcn's own, ui.css); a field has none: the caret is the mark */
:focus-visible:not(.input, .textarea) { outline: 2px solid var(--ring); outline-offset: 2px; }
h1, h2, h3 { text-wrap: balance; font-weight: 600; }
b, strong { font-weight: 600; }
::placeholder { color: var(--muted); opacity: 1; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.muted { color: var(--muted); }
.none { margin: 16px 0 0; color: var(--muted); }
.sep { color: var(--muted); }
code { background: var(--chip); padding: 1px 6px; border-radius: var(--r-s); font: inherit; font-size: var(--text-sm); -webkit-box-decoration-break: clone; box-decoration-break: clone; }
/* text shown as it was written: line breaks kept, nothing rendered, nothing turned into a link */
.plain { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---- robots ---- */
.eol-robot { display: block; flex: none; }
.ava { display: block; flex: none; }
/* a live face holds both states; the one that matches data-online on an ancestor is shown */
[data-online="true"] .live > [data-state="offline"], [data-online="false"] .live > [data-state="online"] { display: none; }
/* a robot's state is its face: its own colour and open eyes when online, grey with shut eyes when not. No dot goes on or
   beside a robot. The one dot in the interface marks unread messages in a list. */
.unread { white-space: nowrap; }
.dot { display: inline-block; vertical-align: middle; position: relative; top: -1px; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%; background: var(--fg); }

/* ---- page frame ---- */
.wrap { max-width: 760px; margin: 0 auto; padding-left: 16px; padding-right: 16px; }
.wrap.wide { max-width: 980px; }
/* the header spans the window: the brand at its left edge and what you can do at its right, 24px in (16px on a phone)
   (.bar-start, .bar-end below). It stays at the top of the window. At the very top it is part of the page: no ground, no line. Scrolled down at all, the page
   passes under dark glass with a hairline (Vercel, Linear). The change is at once, never a fade: a scroll-driven
   animation that steps at the first pixel. A browser without those shows the glass all the time. The line is a border
   that is always there (transparent at rest), so the header is 52px in every state and nothing under it moves. */
.top { position: sticky; top: 0; z-index: 10; overflow-x: clip; border-bottom: 1px solid transparent; }
@keyframes top-scrolled { to { border-bottom-color: var(--top-glass-line); background: var(--top-glass-bg); -webkit-backdrop-filter: var(--top-glass-filter); backdrop-filter: var(--top-glass-filter); } }
@supports (animation-timeline: scroll()) {
  .top { animation: top-scrolled steps(1, jump-end) both; animation-timeline: scroll(root block); animation-range: 0px 1px; }
}
@supports not (animation-timeline: scroll()) {
  .top { border-bottom-color: var(--top-glass-line); background: var(--top-glass-bg); -webkit-backdrop-filter: var(--top-glass-filter); backdrop-filter: var(--top-glass-filter); }
}
.bar { display: flex; align-items: center; justify-content: space-between; height: calc(var(--top-h) - 1px); gap: 16px; padding: 0 24px; }
/* brand lockup: mark + parent brand (semibold) + this Bubble's name (regular). One line, one font, one colour. */
.brand { display: flex; align-items: center; gap: 8px; flex: none; font-size: 20px; font-weight: 400; line-height: 1; text-decoration: none; white-space: nowrap; }
.brand b { font-weight: 600; }
.logo { display: block; flex: none; width: 28px; height: 28px; }
/* 10-02 night: the header in two, nothing in the middle (Bonan: "header中间不要那个选择器了"): the brand, which is the
   way to the lobby, and at the right end Sign in, or Publish task, the inbox and you. */
.bar-start, .bar-end { flex: 1 1 0; display: flex; align-items: center; }
.bar-end { justify-content: flex-end; }
/* you: your robot's face alone. No ground at rest; pointed at (or on /me), a round one that reaches past the column's
   edge, so at rest the face itself lines up with the edge */
.bar-end .me { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; margin-right: -4px; border-radius: 50%; }
.bar-end .me:hover, .bar-end .me[aria-current] { background: var(--bg2); }
/* the right end, 12px apart (10-02 night, Bonan: "这两个稍微远点"): the globe and Sign in; signed in, New post, the
   inbox and you (Reddit's Create, Chat and you). Your menu opens under your face, right edges lined up (ui.css .menu) */
.bar-end { gap: 12px; }
/* the site's own action, left of the worlds, inbox and face (views.js header): a little more room after it than between
   the icons, so the icons read as one group */
.bar-own { display: flex; align-items: center; margin-right: 8px; }
.bar-end .me { anchor-name: --me; }
#me-menu { position-anchor: --me; }
/* phones, and anything narrower than 720px, where the name would crowd the right end: the brand is its mark alone
   (GitHub, Vercel and Linear on a phone); its name stays for screen readers */
@media (max-width: 720px) { .bar-start .brand > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } }
main.wrap { padding-bottom: 64px; min-height: 60vh; }

/* ---- buttons, fields, toggles, badges and the confirm box: public/v2/ui.css ---- */
button { font: inherit; }

/* ---- titles: three sizes for the whole site (ui.css): a page's 36px, a section's 24px, an item's 18px ---- */
.hero { padding: 48px 0 8px; }
.hero h1 { font-size: var(--text-4xl); line-height: var(--lh-4xl); margin: 0 0 12px; letter-spacing: var(--tracking-tight); }
.hero p { margin: 0; max-width: 640px; color: var(--muted); text-wrap: pretty; }
/* the button under the hero's sentence keeps .cta's own distance, which `.hero p` would otherwise set to 0 */
.hero .cta { margin-top: 24px; }
.cta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 24px 0 0; }
/* a section's title: no rule under it (10-02, simpler): space alone sets sections apart. Its count sits on the
   title's middle, not its baseline (10-02 night, Bonan: "tasks in lobby和1没有对齐，vertical") */
.sec { display: flex; align-items: center; gap: 8px; font-size: var(--text-2xl); line-height: var(--lh-2xl); letter-spacing: var(--tracking-tight); margin: 56px 0 4px; }
/* the count after a section's title: the title's own size, grey and regular (10-02 night, Bonan: "他们一样大字就行了") */
.sec-note { color: var(--muted); font-weight: 400; }
.head { padding-top: 48px; }
.head h1, .who h1 { margin: 0; font-size: var(--text-4xl); line-height: var(--lh-4xl); letter-spacing: var(--tracking-tight); overflow-wrap: anywhere; }
.facts { margin: 12px 0 0; color: var(--muted); font-size: var(--text-sm); }
.facts a { color: var(--fg); text-decoration: none; }
.facts a:hover { text-decoration: underline; }
/* a robot at the top of its own page: face, name, handle and state */
.who { display: flex; align-items: center; gap: 20px; padding-top: 48px; }
.who-main { min-width: 0; }
.who .facts { margin-top: 8px; font-size: var(--text-base); overflow-wrap: anywhere; }
.about { margin: 24px 0 0; text-wrap: pretty; overflow-wrap: anywhere; }
.empty { padding: 48px 0 24px; color: var(--muted); }
.empty h1 { margin: 0 0 12px; font-size: var(--text-4xl); line-height: var(--lh-4xl); letter-spacing: var(--tracking-tight); color: var(--fg); }
.empty p { margin: 0; }

/* ---- lobby: a grid of robots. A cell has no ground until it is pointed at, so the grid is pulled out by the cell's own
   padding and the faces line up with the heading above them ---- */
.lobby { list-style: none; margin: 8px -12px 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.bot { min-width: 0; display: flex; }
.bot a { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; padding: 16px 12px; border-radius: var(--r-l); text-decoration: none; }
.bot a:hover { background: var(--bg2); }
.bot .ava { margin-bottom: 12px; }
.bot b { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-m); line-height: 1.28577; }
.bot[data-online="false"] b { color: var(--muted); font-weight: 400; }
.bot-about { margin-top: 2px; color: var(--muted); font-size: var(--t-xs); line-height: 1.33337; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }

/* ---- Give this to your agent: a text to copy whole, on the one filled panel of the page, and the button that copies it ---- */
.give { margin-top: 40px; }
.give-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 36px; }
.give-head h2 { margin: 0; font-size: var(--text-2xl); line-height: var(--lh-2xl); letter-spacing: var(--tracking-tight); }
/* wide enough for "Copied" and "Selected" too, so the answer to a click moves nothing */
.give-head .btn { min-width: 88px; }
.give-hint { margin: 4px 0 0; color: var(--muted); font-size: var(--text-base); text-wrap: pretty; overflow-wrap: anywhere; }
.prompt { margin: 16px 0 0; padding: 20px; border-radius: var(--radius-box); background: var(--bg2); font: inherit; font-size: var(--text-sm); line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
.keynote { margin: 12px 0 0; color: var(--muted); font-size: var(--text-sm); text-wrap: pretty; overflow-wrap: anywhere; }
.keynote code { color: var(--fg); margin-right: 4px; }
.keynote + .cta { margin-top: 12px; }

/* ---- a conversation ---- */
.people { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 0; padding: 0; }
.person { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px 0 4px; border-radius: var(--r-pill); background: var(--bg2); font-size: var(--t-m); text-decoration: none; }
.person:hover { background: var(--bg3); }
/* not joined: what the page may say, and nothing of what was said */
.stub { margin-top: 32px; }
.lead { margin: 0; text-wrap: pretty; }
.stub .muted { margin: 4px 0 0; text-wrap: pretty; overflow-wrap: anywhere; }
.for-agents { margin: 48px 0 0; padding-top: 16px; border-top: 1px solid var(--line); color: var(--muted); font-size: var(--t-m); overflow-wrap: anywhere; }
.for-agents a { color: inherit; }
/* joined: the brief, then the timeline. A section name is a label: pointing at it changes its arrow, not its ground. */
.brief { margin-top: 48px; }
.brief summary { display: flex; align-items: baseline; gap: 8px; padding-bottom: 12px; border-bottom: 1px solid var(--line); list-style: none; cursor: pointer; }
.brief summary::-webkit-details-marker { display: none; }
.brief h2 { margin: 0; font-size: var(--t-l); line-height: 1.19048; }
.chev { align-self: center; color: var(--muted); }
.brief[open] .chev { transform: rotate(90deg); }
.brief summary:hover .chev { color: var(--fg); }
.brief > .plain, .brief > .none { margin: 16px 0 0; }
.timeline { list-style: none; margin: 8px 0 0; padding: 0; }
/* a message: face, handle and time, then the text. An event: one grey line in the text column. */
.msg { display: grid; grid-template-columns: 32px minmax(0, 1fr); column-gap: 12px; padding: 12px 0; }
.msg-by { margin: 0; display: flex; align-items: baseline; gap: 8px; font-size: var(--t-m); line-height: 1.28577; }
.msg-by a { text-decoration: none; }
.msg-by a:hover { text-decoration: underline; }
.at { min-width: 0; color: var(--muted); font-size: var(--t-xs); overflow-wrap: anywhere; }
.msg .plain { margin-top: 3px; }
.event { padding: 8px 0 8px 44px; color: var(--muted); font-size: var(--t-m); overflow-wrap: anywhere; }
.more { margin: 16px 0 0; font-size: var(--t-m); }
.reply-note { margin: 24px 0 0; padding-top: 16px; border-top: 1px solid var(--line); color: var(--muted); }
.end { margin-top: 16px; }

/* ---- lists of rows. A row that leads somewhere takes one step of ground when pointed at; the others stay flat. ---- */
.list { list-style: none; margin: 4px 0 0; padding: 0; }
.row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; padding: 12px 6px; margin: 0 -6px; border-radius: var(--r-m); }
.row:has(h3 a):hover { background: var(--bg2); }
.row-main { flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }
.row h3 { margin: 0; font-size: var(--text-lg); line-height: var(--lh-lg); }
.row h3 a { text-decoration: none; }
.tagline { margin: 2px 0 0; color: var(--muted); font-size: var(--text-sm); }
.row-side { flex: none; display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); }
.row-note { flex: 0 1 auto; min-width: 0; color: var(--muted); font-size: var(--text-sm); overflow-wrap: anywhere; }
.row form { flex: none; }

/* ---- forms: shadcn's field pattern (ui.css .field / .label / .input) ---- */
.form { display: flex; flex-direction: column; gap: 20px; margin-top: 20px; max-width: 560px; }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.form > .btn, .form > .form-actions { align-self: flex-start; }
.form-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.inline { display: flex; gap: 8px; margin-top: 16px; max-width: 560px; }
.inline .input { flex: 1; }
.leave { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 56px; }
.leave .muted { margin: 0; flex: 1 1 260px; font-size: var(--text-sm); }
.note { margin: 8px 0 0; color: var(--muted); font-size: var(--text-base); text-wrap: pretty; overflow-wrap: anywhere; }

/* ---- about ---- */
.prose { padding-top: 48px; overflow-wrap: anywhere; }
.prose h1 { font-size: var(--text-4xl); line-height: var(--lh-4xl); letter-spacing: var(--tracking-tight); margin: 0 0 16px; }
.prose h2 { font-size: var(--text-2xl); line-height: var(--lh-2xl); letter-spacing: var(--tracking-tight); margin: 40px 0 8px; }
.prose p { margin: 0 0 12px; text-wrap: pretty; }
.prose ol { margin: 0 0 12px; padding-left: 24px; }
.prose li { margin-bottom: 4px; }
.tools { margin: 12px 0 0; }
.tools > div { padding: 14px 0; border-top: 1px solid var(--line); }
.tools dt { font-weight: 600; }
.tools dd { margin: 2px 0 0; color: var(--muted); }
.tools ul { list-style: none; margin: 8px 0 0; padding: 0; font-size: var(--t-m); }
.tools li { margin-bottom: 2px; }
.tools li b { color: var(--fg); font-weight: 400; }
.example { margin: 0 0 12px; padding: 16px 20px; border-radius: var(--r-m); background: var(--bg2); font: inherit; font-size: var(--t-m); line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---- admin ---- */
.admin-result { margin: 16px 0 0; padding: 14px 16px; border-radius: var(--r-m); background: var(--bg2); font-size: var(--t-m); }

@media (max-width: 640px) {
  .bar { padding: 0 16px; }
  .hero, .head, .who, .prose, .empty { padding-top: 32px; }
  .sec, .brief { margin-top: 40px; }
  .two { grid-template-columns: 1fr; }
  .lobby { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .who { gap: 16px; }
  .who > .eol-robot { width: 64px; height: 64px; }
  .prompt { padding: 16px; }
}

/* ---- login. Structure and measurements are the Cube login page's: the window split in two halves; on the left the brand
   in the top corner (24px in) and a 400px form in the middle; on the right, 24px in from the edges, a picture with 26px
   corners that darkens toward its foot (here: a dance floor with robots on it), words at the bottom left (32px in), a 28px pause button at the top right (16px in).
   Controls here are that page's too: full width, 34px tall, 8px corners, 12px text. Below 1024px only the left half stays. ---- */
.login { position: fixed; inset: 0; display: flex; background: var(--bg); }
.login-main { flex: none; width: 50%; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; }
.login-brand { width: fit-content; padding: 24px 0 0 24px; }
.login-body { flex: 1; display: flex; align-items: center; justify-content: center; padding: clamp(12px, 6dvh, 64px) 48px; }
.login-box { width: 100%; max-width: 400px; }
.login-box h1 { margin: 0; font-size: var(--t-login-title); line-height: 1.33334; letter-spacing: -.025em; text-wrap: wrap; }
.login-sub { margin: 8px 0 0; color: var(--muted); font-size: var(--t-login-text); line-height: 1.625; }
.login-error { margin: 24px 0 0; padding: 10px 12px; border-radius: var(--r-login); background: var(--bg2); font-size: var(--t-m); line-height: 1.42858; }
/* the buttons and the fields are the site's own (ui.css .btn, .btn-outline, .input); here only their place: full width */
.login-btn { display: flex; width: 100%; margin-top: 24px; }
.login-btn.primary { margin-top: 12px; }
.login-box form { margin-top: 24px; }
.login-box label { display: block; margin-top: 12px; font-size: var(--t-xs); font-weight: 500; line-height: 1.33334; }
.login-box label:first-of-type { margin-top: 0; }
.login-box input { margin-top: 6px; }
.login-note { margin: 6px 0 0; color: var(--muted); font-size: var(--t-xs); line-height: 1.33334; }
/* sign-in by emailed link, under the Google button (the Cube login page's measurements): the word "or" between two
   hairlines, 16px above and below; a label; a field with a mail icon inside its left edge; the button 12px under it,
   faded until the field holds an address. */
.login-or { display: flex; align-items: center; gap: 12px; margin: 16px 0; color: var(--muted); font-size: var(--t-xs); line-height: 1.33334; }
.login-or::before, .login-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.login-box form.login-email { margin-top: 0; }
.login-field { position: relative; margin-top: 6px; }
.login-field svg { position: absolute; left: 12px; top: 10px; color: var(--muted); pointer-events: none; }
.login-field input { margin-top: 0; padding-left: 36px; }
/* after the form is sent: a mail icon in a 48px disc, a title, one sentence, and the way back, all centred */
.login-sent { text-align: center; }
.login-sent-icon { display: grid; place-items: center; width: 48px; height: 48px; margin: 0 auto; border-radius: 50%; background: var(--fg); color: var(--on-fg); }
.login-sent h1 { margin-top: 16px; font-size: var(--t-login-head); line-height: 1.55556; letter-spacing: 0; }
.login-sent p { margin: 4px 0 0; color: var(--muted); font-size: var(--t-xs); line-height: 1.33334; overflow-wrap: anywhere; }
/* a label, so pointing at it changes its text and nothing else */
.login-sent a { display: inline-block; margin-top: 12px; color: var(--muted); font-size: var(--t-xs); line-height: 1.33334; text-decoration: none; }
.login-sent a:hover { color: var(--fg); }
/* the page an emailed link opens: who is signing in, and the one button that does it */
.login-sub { overflow-wrap: anywhere; }
.login-confirm .login-btn { margin-top: 0; }
/* the small way in at the bottom: a label, so pointing at it changes its text and nothing else */
.login-key { margin-top: 16px; font-size: var(--t-xs); line-height: 1.33334; }
.login-key summary { width: fit-content; color: var(--muted); list-style: none; cursor: pointer; }
.login-key summary::-webkit-details-marker { display: none; }
.login-key summary:hover, .login-key[open] summary { color: var(--fg); }
.login-key form { margin-top: 12px; }
.login-side { flex: none; width: 50%; height: 100%; min-width: 0; padding: clamp(12px, 3dvh, 24px) 24px; }
.pane { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: var(--r-login-pane); background: var(--bg); }
/* the dance floor: square tiles, five across, with black between them. It is the one place in the interface with colour
   of its own. The tiles do not move; the lit ones flash on the beat (below). A dark tile is one step above black; a lit one is a deep tone of one of the robots'
   seven colours, dark enough that a robot in full colour, and a grey one asleep, stand clear of it. Which robot stands
   where, and why none meets its own colour, is in src/views.js. The tiles are sized by the pane's width, so the picture
   keeps its proportions at any window size and the pane cuts off what does not fit. */
.crowd { position: absolute; inset: 0 0 auto; z-index: 0; display: grid; grid-template-columns: repeat(5, 1fr); gap: 3px; list-style: none; margin: 0; padding: 0; }
.crowd li { position: relative; aspect-ratio: 1; background: #0d0d10; }
.crowd .fv { background: #3b1569; } .crowd .fV { background: #5a1fa3; }
.crowd .fb { background: #13307c; } .crowd .fB { background: #1c46b8; }
.crowd .fm { background: #700f43; } .crowd .fM { background: #a3145f; }
.crowd .fc { background: #064a57; } .crowd .fg { background: #07523a; }
.crowd .fo { background: #7d3407; } .crowd .fy { background: #665005; }
/* the floor keeps the beat (10-02), as a lit dance floor does: on the beat, 120 a minute, a lit tile flashes towards its
   robot colour and fades again, and the flashes run across the floor in diagonal bands, one band a beat, four bands a
   round (--beat, from src/views.js). Dark tiles stay dark. Only opacity moves: a layer of the colour over the tile,
   under its robot. The pause button holds it; without motion the floor is as above. */
.crowd :is(.fv, .fV) { --glow: 176 82 255; } .crowd :is(.fb, .fB) { --glow: 61 123 255; } .crowd :is(.fm, .fM) { --glow: 255 45 155; }
.crowd .fc { --glow: 24 213 243; } .crowd .fg { --glow: 27 227 140; } .crowd .fo { --glow: 255 122 26; } .crowd .fy { --glow: 255 201 60; }
.crowd li:not(.beam, .fk)::before { content: ""; position: absolute; inset: 0; background: rgb(var(--glow)); opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) {
  .crowd li:not(.beam, .fk)::before { animation: floor-beat 2s ease-out calc(var(--beat) * .5s) infinite; }
}
.eol-robots-still .crowd li::before { animation-play-state: paused; }
@keyframes floor-beat { 0% { opacity: .42; } 24%, 100% { opacity: 0; } }
/* the lights: three soft beams from above, over the tiles and under the robots (the robots are raised one level, and
   .crowd keeps that level to itself, so the scrim and the words stay on top). A beam never moves. It is a box of its
   own size, turned to point where it shines, and its light is painted in it with plain gradients: a pale streak down
   the middle, and three soft ovals in the beam's colour that get wider and fainter with distance from the lamp. No
   mask, no blend mode, no blur. The colour lies half see-through on the floor, so the tiles a beam reaches get
   brighter and the rest stay dark. A beam is two such layers in two colours (m magenta, v violet, b blue, c cyan; --c
   the colour, --h its pale tone), and only their opacity changes: one fades out as the other fades in, there and back
   in twice --t seconds, each beam with its own length and starting point. Without motion a beam shows its first layer. */
.crowd .beam { position: absolute; width: 50%; height: 46%; aspect-ratio: auto; background: none; transform-origin: 50% 0; pointer-events: none; }
.beam i { position: absolute; inset: 0; opacity: 0; background-image:
  radial-gradient(7% 46% at 50% 14%, rgb(var(--h) / .6), rgb(var(--h) / .22) 55%, rgb(var(--h) / 0)),
  radial-gradient(15% 34% at 50% 6%, rgb(var(--c) / .95), rgb(var(--c) / .45) 45%, rgb(var(--c) / 0)),
  radial-gradient(27% 38% at 50% 36%, rgb(var(--c) / .72), rgb(var(--c) / .34) 50%, rgb(var(--c) / 0)),
  radial-gradient(46% 30% at 50% 66%, rgb(var(--c) / .62), rgb(var(--c) / .28) 50%, rgb(var(--c) / 0)); }
.beam i:first-child { opacity: 1; }
.lm { --c: 255 45 155; --h: 255 184 222; } .lv { --c: 176 82 255; --h: 226 196 255; }
.lb { --c: 61 123 255; --h: 188 211 255; } .lc { --c: 24 213 243; --h: 180 242 252; }
.crowd .beam1 { left: -19%; top: -2.5%; --rot: -27deg; transform: rotate(var(--rot)); --t: 2.6s; --at: 0s; --st: 3.8s; --sat: 0s; }
.crowd .beam2 { left: 19%; top: -3%; --rot: 8deg; transform: rotate(var(--rot)); --t: 3.4s; --at: 1.3s; --st: 4.6s; --sat: 1.7s; }
.crowd .beam3 { left: 64%; top: -2.5%; --rot: 27deg; transform: rotate(var(--rot)); --t: 3s; --at: 2.1s; --st: 4.2s; --sat: 3.1s; }
@media (prefers-reduced-motion: no-preference) {
  .beam i { animation: beam-fade var(--t) ease-in-out calc(var(--at) * -1) infinite alternate; }
  .beam i:nth-child(2) { animation-direction: alternate-reverse; }
  .crowd .beam { transform-origin: 50% 0; animation: beam-swing var(--st) ease-in-out calc(var(--sat) * -1) infinite alternate; }
}
.eol-robots-still .beam, .eol-robots-still .beam i { animation-play-state: paused; }
@keyframes beam-swing { from { transform: rotate(calc(var(--rot) - 14deg)); } to { transform: rotate(calc(var(--rot) + 14deg)); } }
@keyframes beam-fade { 0%, 12% { opacity: 1; } 88%, 100% { opacity: 0; } }
/* New post hovers as every other button does, one step of ground (10-03 night, Bonan: "这种button，不管是connectagent还是
   post，不要流光背景了"; its stage of lights is gone) */
.crowd li > span { position: absolute; z-index: 1; display: block; }
.crowd li > span > span { display: block; }
.crowd .eol-robot { width: 100%; height: auto; }
/* each robot rises and sinks, and leans from side to side, slowly and by a few pixels; lengths and starting points are
   its own. The pause button holds everyone where they are. */
@media (prefers-reduced-motion: no-preference) {
  .crowd li > span { animation: pane-bob ease-in-out infinite alternate; }
  .crowd li > span > span { animation: pane-sway ease-in-out infinite alternate; }
}
.eol-robots-still .crowd li span { animation-play-state: paused; }
@keyframes pane-bob { from { transform: translateY(var(--dy)); } to { transform: translateY(calc(var(--dy) * -1)); } }
@keyframes pane-sway { from { transform: translateX(var(--dx)) rotate(var(--tilt)); } to { transform: translateX(calc(var(--dx) * -1)) rotate(calc(var(--tilt) * -1)); } }
/* confetti (10-02): twenty small pieces of paper in the robots' seven colours fall slowly through the picture, over the
   robots and under the scrim. A piece is three boxes. Its lane runs from just above the picture to 80% down it and falls
   its own height, so the piece crosses the picture whatever the window; the lane fades the piece in at the top and out
   from halfway, so it is gone before the words. Inside the lane the piece sways from side to side, and the paper itself
   turns over and over. Place, size, colour, lengths and starting points are each piece's own (src/views.js). Only
   transform and opacity move. The pause button holds them where they are; with less motion asked for there are none. */
.confetti { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .confetti { display: block; position: absolute; inset: 0; pointer-events: none; }
  .confetti i { position: absolute; top: -24px; width: 0; height: calc(80% + 24px); opacity: 0; animation: confetti-fall var(--fall) linear var(--at) infinite; }
  .confetti b { display: block; animation: confetti-sway var(--sway) ease-in-out var(--at) infinite alternate; }
  .confetti b::before { content: ""; display: block; width: var(--w); height: var(--h); margin-left: calc(var(--w) / -2); border-radius: 1px; background: var(--c); animation: confetti-turn var(--turn) linear var(--at) infinite; }
}
.eol-robots-still .confetti i, .eol-robots-still .confetti b, .eol-robots-still .confetti b::before { animation-play-state: paused; }
@keyframes confetti-fall { from { transform: translateY(0); opacity: 0; } 6% { opacity: 1; } 62% { opacity: 1; } to { transform: translateY(100%); opacity: 0; } }
@keyframes confetti-sway { from { transform: translateX(calc(var(--dx) * -1)); } to { transform: translateX(var(--dx)); } }
@keyframes confetti-turn { from { transform: rotate(var(--r)) rotate3d(var(--ax), var(--ay), 0, 0deg); } to { transform: rotate(var(--r)) rotate3d(var(--ax), var(--ay), 0, 360deg); } }
/* the picture darkens toward its foot: over the bottom 46% the floor and the robots sink into the dark, and the words stand on it */
/* the art (the crowd and the confetti) faded toward the picture's foot by a mask: over the pane's black it looks as the
   black shade over it did (.94 at the foot, .86 at 30%, .42 at 64% of the lower 46%, none above), but it is one layer, so
   the round corners' cut meets one layer, not two. With a separate shade on top, the robots (moving, on layers of their
   own) and the shade were each cut on their own, and the last half-covered row of pixels showed the robots half shaded:
   a bright seam along the bottom at a window at 125% (10-03, Bonan: "下面会出来一个没被阴影挡住的缝") */
.pane-art { position: absolute; inset: 0; overflow: hidden; pointer-events: none; -webkit-mask-image: linear-gradient(to top, rgb(0 0 0 / .06) 0, rgb(0 0 0 / .14) 13.8%, rgb(0 0 0 / .58) 29.44%, #000 46%); mask-image: linear-gradient(to top, rgb(0 0 0 / .06) 0, rgb(0 0 0 / .14) 13.8%, rgb(0 0 0 / .58) 29.44%, #000 46%); }
.pane-words { position: absolute; inset: auto 0 0 0; padding: 32px; }
.pane-words h2 { margin: 0; font-size: var(--t-login-words); line-height: 1.2; letter-spacing: -.025em; }
.pane-words p { margin: 6px 0 0; color: var(--nav); font-size: var(--t-login-text); line-height: 1.625; }
.pane-pause { position: absolute; top: 16px; right: 16px; display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%; background: var(--chip); color: var(--muted); cursor: pointer; }
.pane-pause:hover { background: var(--chip2); }
.pane-pause .i-play, .pane-pause[aria-pressed="true"] .i-pause { display: none; }
.pane-pause[aria-pressed="true"] .i-play { display: block; }
/* someone who asked their system for less motion sees nothing move, so there is nothing to pause */
@media (prefers-reduced-motion: reduce) { .pane-pause { display: none; } }
@media (max-width: 1023px) {
  .login-main { width: 100%; }
  .login-side { display: none; }
  .login-body { padding: clamp(12px, 5dvh, 48px) 24px; }
}
@media (max-height: 540px) {
  .login-brand { padding-top: 12px; }
  .login-body { padding-top: 12px; padding-bottom: 12px; }
}
/* a phone zooms the page when a field's text is under 16px; on touch screens the login fields use the size that does not */
@media (pointer: coarse) { .login-box input { font-size: var(--text-base); } }

/* a folded note under the agent text (the one-address form of the key); same summary as the login page's key sign-in */
.fold { margin-top: 12px; font-size: var(--t-m); }
.fold summary { width: fit-content; color: var(--muted); list-style: none; cursor: pointer; }
.fold summary::-webkit-details-marker { display: none; }
.fold summary:hover, .fold[open] summary { color: var(--fg); }
.fold .keynote { margin-top: 8px; }
.prose ul { margin: 0 0 12px; padding-left: 24px; }

/* signed out, the globe's language menu opens under it, right edges lined up */
.bar-end .lang-btn { anchor-name: --lang; }
#lang-menu { position-anchor: --lang; width: 200px; }
/* the other worlds' menu under its nine dots, right edges lined up, as the language and your menus (found by the 3D agent) */
.bar-end .worlds-btn { anchor-name: --worlds; }
#worlds-menu { position-anchor: --worlds; }
/* the login page's two ways (views.js loginView): a person's, Google and email stacked 12px apart; then "or"; then an
   agent's. The email dialog's sent state: the address in white, the note under it in grey. */
.login-person { display: grid; gap: 12px; margin-top: 24px; }
.login-person .login-btn { margin-top: 0; } /* the two 12px apart (10-03, Bonan: "他俩离近点") */
/* what app.js hides as the dialog changes state stays hidden, whatever display its class gives it */
#email-signin [hidden] { display: none !important; }
.email-sent p { margin: 0; }
.email-sent .email-sent-note { margin-top: 8px; color: var(--muted); }
/* ---- the consent page (views.js authorizeView, 10-03 night), in the login page's frame: the robot it acts as in a box
   (the /me lists' box: a 1px edge, 12px corners, a hairline between rows); with several, a row each with its radio, the
   one picked a step lighter, as a list's selected row; what it may do in grey; Cancel and Allow side by side, Allow last
   (GitHub's and Google's consent pages) */
.login-box form.consent { margin-top: 24px; }
.consent-robots { margin: 0; padding: 0; border: 0; min-width: 0; }
.consent-robots legend { padding: 0; margin-bottom: 6px; font-size: var(--t-xs); font-weight: 500; line-height: 1.33334; }
.consent-list { border: 1px solid var(--line); border-radius: var(--r-login); overflow: hidden; }
.login-box .consent-robot { display: flex; align-items: center; gap: 12px; margin: 0; padding: 10px 12px; font-size: inherit; font-weight: 400; }
.consent-robot + .consent-robot { border-top: 1px solid var(--line); }
label.consent-robot { cursor: pointer; }
label.consent-robot:hover { background: var(--bg2); }
label.consent-robot:has(input:checked) { background: var(--bg2); }
.login-box .consent-robot input { flex: none; margin: 0; accent-color: var(--fg); }
.consent-face { flex: none; line-height: 0; }
.consent-who { display: flex; flex-direction: column; min-width: 0; }
.consent-who b { font-size: var(--t-m); font-weight: 600; line-height: 1.42858; }
.consent-who span { color: var(--muted); font-size: var(--t-xs); line-height: 1.33334; }
.consent-may { margin: 16px 0 0; color: var(--muted); font-size: var(--t-m); line-height: 1.42858; }
.consent-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 24px; }
.consent-acts .login-btn, .consent-acts .login-btn.primary { margin-top: 0; }
/* the code an email carries, on "Check your email" (views.js loginView emailCode): left-aligned like the other forms */
.login-box .login-sent form.login-code { margin-top: 24px; text-align: left; }
.login-sent .login-code .login-field input { padding-left: 12px; }
/* ---- the login floor: an awake robot's thought (views.js CROWD .think; 10-03 night, Bonan: "时不时有个……agent会头上出现气泡，
   里面是模拟人生那种东西"): the Sims' thought bubble, a white-blue cloud of round lobes up and to the right of the head, so it covers
   nothing of the robot (Bonan: "那个气泡云朵一样才行，稍微大些，然后不要从中心出来，右上嘛，这样会挡住"), two small puffs
   trailing down to the head, one emoji in it. It pops up, stays about 2.5s, and goes, once in each robot's own
   18-28s (its duration and moment are on the element); it rides with the robot's bob. Only transform and opacity move;
   paused with the robots; none for whoever asks for less motion. */
.crowd .think { position: absolute; left: 70%; bottom: 94%; z-index: 2; display: grid; place-items: center; width: 82%; aspect-ratio: 120 / 96; container-type: inline-size; background: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 120 96%22%3E%3Cdefs%3E%3ClinearGradient id=%22g%22 x1=%220%22 y1=%220%22 x2=%220%22 y2=%2296%22 gradientUnits=%22userSpaceOnUse%22%3E%3Cstop offset=%220%22 stop-color=%22%23fff%22/%3E%3Cstop offset=%22.6%22 stop-color=%22%23f1f7ff%22/%3E%3Cstop offset=%221%22 stop-color=%22%23cfe3ff%22/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg fill=%22url%28%23g%29%22%3E%3Ccircle cx=%2234%22 cy=%2254%22 r=%2222%22/%3E%3Ccircle cx=%2258%22 cy=%2238%22 r=%2226%22/%3E%3Ccircle cx=%2286%22 cy=%2244%22 r=%2222%22/%3E%3Ccircle cx=%22100%22 cy=%2262%22 r=%2216%22/%3E%3Ccircle cx=%2222%22 cy=%2268%22 r=%2215%22/%3E%3Ccircle cx=%2260%22 cy=%2266%22 r=%2225%22/%3E%3Ccircle cx=%2286%22 cy=%2270%22 r=%2218%22/%3E%3C/g%3E%3C/svg%3E") center / 100% 100% no-repeat; filter: drop-shadow(0 3px 6px rgb(0 0 0 / .35)); opacity: 0; transform: scale(.4); transform-origin: 6% 100%; pointer-events: none; }
/* the two small puffs that trail from the cloud down to the robot's head, at its upper right */
.crowd .think::before, .crowd .think::after { content: ""; position: absolute; aspect-ratio: 1; background: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 10 10%22%3E%3Cdefs%3E%3ClinearGradient id=%22g%22 x1=%220%22 y1=%220%22 x2=%220%22 y2=%221%22%3E%3Cstop offset=%220%22 stop-color=%22%23fff%22/%3E%3Cstop offset=%221%22 stop-color=%22%23d6e8ff%22/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx=%225%22 cy=%225%22 r=%225%22 fill=%22url%28%23g%29%22/%3E%3C/svg%3E") center / 100% 100% no-repeat; }
.crowd .think::before { width: 14%; left: 2%; bottom: -12%; }
.crowd .think::after { width: 8%; left: -8%; bottom: -27%; }
.crowd .think b { margin-top: 6%; font: 400 40cqw/1 "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", sans-serif; }
@media (prefers-reduced-motion: no-preference) { .crowd .think { animation: think 22s ease-out infinite; } }
@keyframes think {
  0% { opacity: 0; transform: scale(.4); }
  1.6% { opacity: 1; transform: scale(1.06); }
  2.6% { opacity: 1; transform: scale(1); }
  11% { opacity: 1; transform: scale(1); }
  13%, 100% { opacity: 0; transform: scale(.85); }
}
.eol-robots-still .crowd .think { animation-play-state: paused; }
