/*
 * Design tokens for the Sally demo.
 *
 * The layout, type sizes, spacing, radii and shadows follow an Airbnb community
 * UI kit, which is the reference issue #54 chose. So do the neutrals. The
 * accent is not the kit's: `--2m-primary` is the apricot Arun picked, #ff9770.
 *
 * Text on it is `--2m-on-primary`, white. That is about 2.1:1, well under
 * the 4.5:1 AA asks of body-sized text: Arun chose it for the look, over a dark
 * ink that passed. Accent-coloured text on white is the darker
 * `--2m-active`, which passes.
 *
 * `--2m-lotus-white` is the one colour kept from the earlier palette. It is
 * the cream paper the illustrations are drawn on, so it sits under every
 * picture slot and nowhere else.
 */
:root {
  --2m-primary: #ff9770;
  --2m-on-primary: #ffffff;
  --2m-active: #b84a1c;
  --2m-lotus-white: #fbecde;

  --2m-black: #000000;
  --2m-ink: #222222;
  --2m-ink-soft: #6a6a6a;
  --2m-line: #dddddd;
  --2m-fill: #f2f2f2;
  --2m-surface: #ffffff;
  --2m-danger: #9a3b46;

  --2m-radius-card: 24px;
  --2m-radius-art: 20px;
  --2m-radius-control: 8px;
  --2m-shadow-card: 0 4px 12px rgba(0, 0, 0, 0.12);

  --2m-space-1: 8px;
  --2m-space-2: 16px;
  --2m-space-3: 24px;
  --2m-space-4: 32px;
  /* How tall the floating "Level up" button is, and so how much room the Twin
     tab leaves above itself for it. */
  --2m-level-up: 40px;

  /* The bar's own height, without the home indicator's strip under it. Shared
     because the screens it floats over have to end above it. */
  --2m-tab-bar: 65px;

  /* The kit's face is Graphik, which is a paid font. System sans stands in. */
  --2m-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;

  color-scheme: light;
}

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

/* `hidden` has to win over any `display` set below. Without this, `.chat`'s
   flex kept the hidden Interview on screen, full height, over the Twin. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--2m-surface);
  color: var(--2m-ink);
  font: 400 16px/1.5 var(--2m-body);
  -webkit-text-size-adjust: 100%;
}

/* Mobile first: one column, 24px gutters, capped on a wider screen so the demo
   is still readable when Arun puts it on a laptop. */
.screen {
  max-width: 480px;
  margin: 0 auto;
  padding: calc(var(--2m-space-4) + env(safe-area-inset-top)) var(--2m-space-3)
    calc(var(--2m-space-4) + env(safe-area-inset-bottom));
}

/* A screen with the tab bar over its bottom edge ends above the bar rather than
   under it, so the last card is reachable and not half-covered. */
.screen.tabbed {
  padding-bottom: calc(var(--2m-tab-bar) + var(--2m-space-4) + env(safe-area-inset-bottom));
}

/* One rhythm down every screen of the app: the head, then each section. */
.tabbed > * + *,
.date-body > * + *,
.card + .card {
  margin-top: var(--2m-space-3);
}

/* A screen's title. The kit has no nav bar: a big left-aligned title instead. */
.wordmark {
  margin: 0;
  color: var(--2m-black);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.kicker {
  margin: 0;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

.lede {
  margin: var(--2m-space-1) 0 var(--2m-space-3);
  color: var(--2m-ink-soft);
}

.screen-head .lede {
  margin-bottom: 0;
  font-size: 14px;
}

.card {
  padding: var(--2m-space-2);
  border-radius: var(--2m-radius-card);
  background: var(--2m-surface);
  box-shadow: var(--2m-shadow-card);
}

.card > :first-child {
  margin-top: 0;
}

.card > :last-child {
  margin-bottom: 0;
}

.card p {
  margin: 0 0 var(--2m-space-1);
}

a {
  color: var(--2m-ink);
  text-underline-offset: 3px;
}

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

h2 {
  margin: 0 0 4px;
  color: var(--2m-black);
  font-size: 18px;
  font-weight: 500;
  line-height: 1.5;
}

h3 {
  font-weight: 500;
}

code {
  overflow-wrap: anywhere;
  border-radius: 4px;
  background: var(--2m-fill);
  padding: 1px 5px;
  font-size: 0.9em;
}

label {
  display: block;
  margin: var(--2m-space-2) 0 var(--2m-space-1);
  font-size: 14px;
  font-weight: 500;
}

input[type="text"],
input[type="email"],
textarea {
  display: block;
  width: 100%;
  border: 1px solid var(--2m-line);
  border-radius: var(--2m-radius-control);
  padding: 13px 14px;
  background: var(--2m-surface);
  color: inherit;
  /* 17px keeps iOS Safari from zooming the page on focus. */
  font: 17px/1.4 var(--2m-body);
  resize: none;
}

/* A consent box (#181): the box beside its words, which run to several lines
   and stay normal-weight body text, links and all. */
.consent-box {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: var(--2m-space-2);
}

.consent-box input {
  flex: none;
  margin: 2px 0 0;
  width: 20px;
  height: 20px;
  accent-color: var(--2m-primary);
}

.consent-box label {
  margin: 0;
  font-size: 15px;
  font-weight: 400;
}

/* A short list of choices under a legend that reads like the labels above it. */
.choices {
  margin: var(--2m-space-2) 0 0;
  border: 0;
  padding: 0;
  min-width: 0;
}

.choices legend {
  margin-bottom: var(--2m-space-1);
  padding: 0;
  font-size: 14px;
  font-weight: 500;
}

/* A whole row to tap, 44 high, which is the smallest a thumb reliably hits. */
.choice {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  min-height: 44px;
  font-size: 17px;
  font-weight: 400;
}

.choice input {
  flex: none;
  margin: 0;
  width: 20px;
  height: 20px;
  accent-color: var(--2m-primary);
}

.choices .note {
  margin: var(--2m-space-1) 0 0;
}

/* The kit's button: filled, white 16 Medium, 14/16 padding, so 52 high. */
button {
  margin-top: var(--2m-space-2);
  width: 100%;
  border: 0;
  border-radius: var(--2m-radius-control);
  padding: 14px 16px;
  background: var(--2m-primary);
  color: var(--2m-on-primary);
  font: 500 16px/1.5 var(--2m-body);
  cursor: pointer;
}

button:disabled {
  opacity: 0.5;
}

/* A link that does what a button does, a step down from it: Sign in with
   Google, under the email form (#182). Outlined, the same size as a button. */
.button-secondary {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border: 1px solid var(--2m-ink);
  border-radius: var(--2m-radius-control);
  padding: 13px 16px;
  color: var(--2m-ink);
  font: 500 16px/1.5 var(--2m-body);
  text-align: center;
  text-decoration: none;
}

/* The word between two ways of doing one thing. */
.or {
  margin: var(--2m-space-2) 0 0;
  color: var(--2m-ink);
  font-size: 14px;
  text-align: center;
}

/* The waitlist's honeypot (#154): there for a form filler, and nowhere a
   person can see or reach. Not `display: none`, which some fillers skip. */
.honeypot {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.error {
  margin: var(--2m-space-2) 0 0;
  color: var(--2m-danger);
  font-size: 14px;
}

/* The Interview, as a messaging app: a slim bar at the top, the turns beneath
   it, and the composer pinned at the bottom of the screen where a thumb is. */
.chat {
  display: flex;
  flex-direction: column;
  /* dvh rather than vh: iOS Safari's toolbars change the viewport height as the
     page scrolls, and the composer has to stay above them, not under them. */
  min-height: 100dvh;
  padding-top: 0;
  padding-bottom: 0;
}

/* Both bars run to the edge of the screen while their contents stay in the
   column's gutters, so the turns scroll behind them rather than beside them. */
.chat-bar,
.composer {
  position: sticky;
  margin: 0 calc(-1 * var(--2m-space-3));
  padding-left: var(--2m-space-3);
  padding-right: var(--2m-space-3);
  background: var(--2m-surface);
}

.chat-bar {
  top: 0;
  z-index: 1;
  border-bottom: 1px solid var(--2m-line);
  padding-top: calc(var(--2m-space-2) + env(safe-area-inset-top));
  padding-bottom: var(--2m-space-2);
}

/* The title and, in a Level Up, the way back to the Twin, on one line. */
.chat-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--2m-space-2);
}

.back-to-twin {
  flex: none;
  margin: 0;
  width: auto;
  min-height: 44px;
  padding: 8px 16px;
  font-size: 14px;
}

.chat-title {
  margin: 0;
  color: var(--2m-black);
  font-size: 18px;
  font-weight: 500;
  line-height: 1.5;
}

.chat .turns {
  flex: 1 1 auto;
  margin: var(--2m-space-2) 0;
  padding: 0;
  list-style: none;
}

.composer {
  bottom: 0;
  border-top: 1px solid var(--2m-line);
  padding-top: var(--2m-space-2);
  /* The iPhone's home indicator sits over the bottom of the screen. */
  padding-bottom: calc(var(--2m-space-2) + env(safe-area-inset-bottom));
}

/* One rounded box with Send inside its bottom-right corner, as in Messages.
   It starts one line high and grows with the answer to five lines, then
   scrolls; app.js sets the height. Picked from five layouts in the composer
   spike (docs/spikes.md): beside the box, a 52px button never lined up with a
   two-line textarea. */
.composer-field {
  position: relative;
}

.composer textarea {
  min-height: 48px;
  /* Five lines of 24, the padding and the border. */
  max-height: 144px;
  border-radius: 24px;
  padding: 11px 50px 11px 18px;
  line-height: 24px;
  overflow-y: hidden;
}

/* One line high when empty, so the hint must not wrap onto a second. */
.composer textarea::placeholder {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Inside the box rather than around it: drawn outside, the ring stands proud
   of the box's edge. */
.composer textarea:focus-visible {
  outline-offset: -2px;
}

/* 36 round, 6 in from the box's corner, so centred on a one-line box. */
.composer button {
  position: absolute;
  right: 6px;
  bottom: 6px;
  display: grid;
  place-items: center;
  margin: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  padding: 0;
}

.turn {
  margin-bottom: var(--2m-space-2);
  max-width: 85%;
  padding: 12px 16px;
  border-radius: var(--2m-radius-art);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.turn-interviewer {
  background: var(--2m-fill);
}

.turn-member {
  margin-left: auto;
  background: var(--2m-ink);
  color: #fff;
}

/* Long, and meant to be copied, so it wraps rather than overflows
   and is not underlined into illegibility. */
.private-link {
  display: inline-block;
  overflow-wrap: anywhere;
  font-weight: 500;
}

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

/* The app's bottom bar: three tabs, always in the same order, pinned where a
   thumb already is. Fixed rather than sticky: it is the app's furniture and does
   not belong to any one screen's scroll. The inline padding keeps the three tabs
   over the column on a screen wider than a phone. */
.tab-bar {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 2;
  display: flex;
  gap: var(--2m-space-2);
  border-top: 1px solid var(--2m-line);
  background: var(--2m-surface);
  padding: 0 max(var(--2m-space-1), calc((100% - 480px) / 2));
  /* The iPhone's home indicator sits over the bottom of the screen. */
  padding-bottom: env(safe-area-inset-bottom);
}

/* The kit's Navbar: a 24px icon, 2px, then a 10px label. */
.tab {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  height: var(--2m-tab-bar);
  margin-top: 0;
  border-radius: 0;
  padding: 0;
  background: none;
  color: var(--2m-ink-soft);
  font: 400 10px/1.5 var(--2m-body);
}

.tab svg {
  flex: none;
  width: 24px;
  height: 24px;
  fill: var(--2m-black);
}

/* The tab the Member is on. The same attribute the screen reader is told by, so
   the two cannot say different things. */
.tab[aria-current] {
  color: var(--2m-active);
}

.tab[aria-current] svg {
  fill: var(--2m-active);
}

/* The top of the Twin tab: a square profile image, then who this is and how far
   they have got. The image is a slot like every other picture in the demo, and
   it is empty: nothing is drawn for a Member yet, so it shows the placeholder. */
.banner {
  display: flex;
  align-items: center;
  gap: var(--2m-space-2);
  border: 1px solid #ebebeb;
  border-radius: var(--2m-radius-card);
  background: var(--2m-surface);
  padding: var(--2m-space-2);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
}

.banner-art {
  position: relative;
  flex: none;
  overflow: hidden;
  width: 96px;
  aspect-ratio: 1;
  border-radius: var(--2m-radius-art);
  background: linear-gradient(150deg, #e0dedd, #f5f4f4);
}

.banner-art::before {
  content: "";
  position: absolute;
  top: 10%;
  left: 38%;
  width: 62%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
}

.banner-who {
  display: grid;
  flex: 1;
  gap: 4px;
  min-width: 0;
}

.banner-name {
  margin: 0;
  color: var(--2m-ink);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

/* "Lv N", beside the Member's name. A pill and not a bar: how far along a
   Member is gets said once, as a number, and nothing on the page fills up. */
.lv {
  justify-self: start;
  margin: 0;
  border-radius: 24px;
  background: var(--2m-primary);
  padding: 3px 10px;
  color: var(--2m-on-primary);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
}

/* A flat section of the Twin tab: a heading, a sentence, then the thing. */
.section > :last-child {
  margin-bottom: 0;
}

.section h2 {
  font-size: 20px;
  line-height: 1.3;
}

/* The sentence under a heading, saying what the Member is looking at. */
.note {
  margin: 0 0 var(--2m-space-2);
  color: var(--2m-ink-soft);
  font-size: 14px;
}

.banner .note {
  margin: 0;
}

/* "Level up", after the UI prototype's: a black pill, small, in the mono face.
   There it floats over the top left of every screen; here over the Twin tab
   only, which is the tab a Level belongs to. A placeholder for #53.

   Fixed, so it stays put while the tab scrolls under it, and held to the same
   480px column as the screen. The tab starts below it, so at the top of the
   page it covers nothing. */
.level-up-float {
  position: fixed;
  top: calc(var(--2m-space-2) + env(safe-area-inset-top));
  left: max(var(--2m-space-3), calc((100% - 480px) / 2 + var(--2m-space-3)));
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--2m-space-1);
  margin: 0;
}

/* The room the tab leaves for it. Taken off on a look, where there is no
   button to leave room for. */
.under-level-up {
  padding-top: calc(var(--2m-space-2) + var(--2m-level-up) + env(safe-area-inset-top));
}

/* What a tap says. Over whatever the tab has scrolled to, so it carries its
   own ground. One line at any width: a pill that wraps is not a pill. */
.level-up-float .note {
  margin: 0;
  border-radius: 999px;
  background: var(--2m-surface);
  padding: 4px 12px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
  white-space: nowrap;
}

/* Nothing to see until there are words in it, but still in the page: a live
   region that is not displayed is not announced when the words arrive. */
.level-up-float .note:empty {
  background: none;
  padding: 0;
  box-shadow: none;
}

.level-up {
  margin: 0;
  width: auto;
  min-height: var(--2m-level-up);
  border-radius: 999px;
  background: #111;
  padding: 0 14px;
  color: #fff;
  font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* The portrait: the first thing a Member reads about themselves, and the only
   long-form prose on the tab. Body size and a loose line, so it reads like a
   page rather than like output. */
.portrait {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
}

/* Four lines until the Member asks for the rest, so the tab opens on all of its
   sections rather than on one long paragraph. */
.portrait.clamped {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
}

/* "Read all": a text button, quiet beside the filled ones. */
.more {
  width: auto;
  margin-top: var(--2m-space-1);
  border-radius: 0;
  background: none;
  padding: 0;
  color: var(--2m-ink);
  font-size: 14px;
  text-decoration: underline;
}

/* The Member's own words, laid out as a cloud: a wrapping run of terms rather
   than a list, because the shape is the point and a phone is 320px wide. */
.word-cloud {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Sized by weight, which is how characteristic of this person the term is and
   not how often they said it. 0 and 1 are 14px and 28px apart; the opacity floor
   keeps the lightest term readable. */
.term {
  --term-weight: 0.5;
  font-size: calc(14px + var(--term-weight) * 14px);
  overflow-wrap: anywhere;
  color: var(--2m-black);
  font-weight: 500;
  line-height: 1.3;
  opacity: calc(0.7 + var(--term-weight) * 0.3);
}

.parameters,
.dictionary {
  margin: 0;
  padding: 0;
  list-style: none;
}

.parameter {
  margin-top: var(--2m-space-2);
  border-top: 1px solid var(--2m-line);
  padding-top: var(--2m-space-2);
}

.parameter-heading {
  margin: 0;
  color: var(--2m-black);
  font-size: 16px;
  line-height: 1.5;
}

/* Whether this reading would be hidden from the Member in the real product. Two
   states, told apart by the words: a caption, and never colour alone. */
.flag {
  margin: 0 0 4px;
  color: var(--2m-ink-soft);
  font-size: 12px;
}

/* Purple marks the hidden ones: a light purple that still keeps over 4.5:1
   on white at this size (5.1:1). */
.flag-hidden {
  color: #7c5cbf;
}

.card .plain-words {
  margin: 0;
}

/* A Parameter no answer has touched. Quieter than a reading, because it is the
   absence of one. */
.card .not-known {
  margin: 0;
  color: var(--2m-ink-soft);
}

.card .confidence {
  margin: 4px 0 0;
  color: var(--2m-ink-soft);
  font-size: 12px;
}

/* The Member's own sentence, as they said it. Set apart and unedited, so it
   reads as evidence for the line above rather than as more of it. */
.evidence {
  overflow-wrap: anywhere;
  margin: var(--2m-space-1) 0 0;
  border-left: 2px solid var(--2m-line);
  padding-left: 12px;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

.evidence::before {
  content: "\201C";
}

.evidence::after {
  content: "\201D";
}

.word {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--2m-space-1);
  color: var(--2m-black);
  font-weight: 500;
}

.dictionary .word + .meaning {
  margin: 0;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

.meaning + .word {
  margin-top: var(--2m-space-2);
}

/* The controlled value behind the sentence. Monospaced and quiet: it is on screen
   so a Member can see what a later Dictionary Collision will be computed from,
   not to be read as prose. */
.sense-code {
  overflow-wrap: anywhere;
  border-radius: 4px;
  background: var(--2m-fill);
  padding: 4px 6px;
  color: var(--2m-ink);
  font: 500 11px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* What the Member stated about who they want to meet, below their cards: one
   sentence, and beside it the way to change it. The button is small because the
   cards are the point of the tab, and still 44 high for a thumb. */
.stated-summary {
  display: flex;
  align-items: center;
  gap: var(--2m-space-2);
}

.stated-summary p {
  flex: 1;
  margin: 0;
  font-size: 14px;
}

.stated-summary .quiet-button {
  flex: none;
  margin: 0;
  width: auto;
  min-height: 44px;
  padding: 8px 16px;
  font-size: 14px;
}

/* The Matches tab, after the kit's listing card: a large picture, then the name
   and a line, with no border or shadow round the lot. One per row: on a phone
   that is the right amount to look at a person at a time. */
.match-list {
  /* The top margin is the screen's rhythm, restated: a bare `margin: 0` here
     would take it off and put the first picture against the head. */
  margin: var(--2m-space-3) 0 0;
  padding: 0;
  list-style: none;
}

.match + .match {
  margin-top: var(--2m-space-4);
}

/* The card's illustration, and its space before there is one. A large square.
   The stored picture is wider than that, a square less the bottom tenth cropped
   away to take off the fake signature, so a little of each side is covered. The
   cream is the paper the illustrations are drawn on, and it stays under them. */
.match-art {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--2m-radius-art);
  background-color: var(--2m-lotus-white);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

.match-name {
  margin: var(--2m-space-1) 0 0;
  font-size: 16px;
  line-height: 1.3;
}

/* An age, not a score. After the name, because the name is the thing being
   introduced. */
.match-age::before {
  content: ", ";
}

.match-gist {
  margin: 6px 0 0;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

/* Says, in words, that this person is invented. The kit's badge, pinned to the
   top left of the picture, which is the first place an eye lands on the card. */
/* A look from the admin panel (`?look`): one line across the top of whatever
   screen is up, so nobody takes the page for the Member's own visit. */
.look-only {
  position: sticky;
  top: 0;
  z-index: 3;
  margin: 0;
  background: var(--2m-ink);
  padding: var(--2m-space-1) var(--2m-space-2);
  color: var(--2m-surface);
  font-size: 13px;
  text-align: center;
}

.flag-synthetic {
  position: absolute;
  top: 10px;
  left: 10px;
  margin: 0;
  border: 1px solid #fff;
  border-radius: 24px;
  background: linear-gradient(#f9f9f9, #f5f5f5);
  padding: 6px 10px;
  color: var(--2m-black);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  filter: drop-shadow(0 3px 2px rgba(0, 0, 0, 0.16));
}

/* The button under a match, which is where a Virtual Date begins. The only
   filled button on the tab: the one thing to do with someone you have finished
   reading about. */
.date-button {
  display: block;
}

/* The Virtual Date. The scene reads as a column rather than as an interface:
   a loose line, and paragraphs with room between them. */
.scene {
  line-height: 1.6;
}

.card .scene-paragraph {
  margin: 0;
}

.card .scene-paragraph + .scene-paragraph {
  margin-top: var(--2m-space-2);
}

/* One of the date's illustrations, laid between two paragraphs of the scene. The
   same kind of slot the Matches tab uses, on the same cream paper, so no screen
   has an image element a photograph could arrive in. The stored picture's own
   shape here, where Matches crops it square. Full width of the card, because a
   drawing in a column of prose is a break in the reading rather than a
   decoration beside it. */
.scene-art {
  aspect-ratio: 10 / 9;
  margin: var(--2m-space-3) 0;
  border-radius: var(--2m-radius-art);
  background-color: var(--2m-lotus-white);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Where it flowed: plain sentences, one per line, quiet next to the snags. */
.flowed,
.snags {
  margin: 0;
  padding: 0;
  list-style: none;
}

.flowed-note {
  border-left: 2px solid var(--2m-line);
  padding-left: 12px;
}

.flowed-note + .flowed-note {
  margin-top: var(--2m-space-2);
}

/* Where it snagged. One block per Dictionary Collision: the word, what it means
   to each of them with its Sense Code, then the note. The accent's rule down the
   side, because a snag is the interesting half. */
.snag {
  border-left: 2px solid var(--2m-primary);
  padding-left: 12px;
}

.snag + .snag {
  margin-top: var(--2m-space-3);
}

.snag-word {
  margin: 0;
  color: var(--2m-black);
  font-size: 18px;
  line-height: 1.5;
}

/* Each side's meaning, in their own words. The sense code sits after it the way
   it does on the Twin tab, so the two screens read as the same vocabulary. */
.card .snag-meaning {
  margin: 4px 0 0;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

.snag-meaning .sense-code {
  margin-left: var(--2m-space-1);
}

.card .snag-note {
  margin: var(--2m-space-1) 0 0;
}

/* The Dates tab before there is a date, after the kit's Trips screen: a grey
   sketch of the list to come, then two lines saying how to fill it. No button:
   the way to a date is the Matches tab, which is one thumb away. */
.dated-empty {
  display: grid;
  justify-items: center;
  gap: 48px;
  padding-top: var(--2m-space-3);
  text-align: center;
}

.skeleton {
  display: grid;
  gap: var(--2m-space-1);
  width: 208px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.skeleton li {
  display: flex;
  align-items: center;
  gap: var(--2m-space-1);
  border-radius: 16px;
  background: var(--2m-surface);
  padding: var(--2m-space-1);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.05);
}

.skeleton li::before {
  content: "";
  width: 48px;
  height: 48px;
  border-radius: 8px;
  background: #d9d9d9;
}

.skeleton li::after {
  content: "";
  width: 128px;
  height: 12px;
  border-radius: 4px;
  background: #f5f4f4;
}

.skeleton .skeleton-on {
  box-shadow: var(--2m-shadow-card);
}

.skeleton .skeleton-on::after {
  background: #d9d9d9;
}

.dated-empty .note {
  margin: 0;
}

/* One row per evening: a thumbnail of its first illustration, the match's
   name and the line the scene opens on. The whole row is the button: a date you
   have been on is only there to be opened again. */
.dated {
  margin: var(--2m-space-3) 0 0;
  padding: 0;
  list-style: none;
}

.dated-entry + .dated-entry {
  margin-top: var(--2m-space-1);
}

.dated-open {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 2px var(--2m-space-2);
  align-items: center;
  margin-top: 0;
  border-radius: 16px;
  background: var(--2m-surface);
  padding: var(--2m-space-1);
  box-shadow: var(--2m-shadow-card);
  color: inherit;
  font-weight: 400;
  text-align: left;
}

/* The date's first drawing, cropped square here rather than at its own ratio:
   this is a thumbnail in a row, and the rows want to be the same height. */
.dated-art {
  grid-row: 1 / span 2;
  aspect-ratio: 1;
  border-radius: 8px;
  background-color: var(--2m-lotus-white);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

.dated-name {
  color: var(--2m-black);
  font-weight: 500;
  line-height: 1.3;
}

/* The first line of the scene, clamped to two lines: enough to tell two evenings
   with the same person apart, and not enough to read instead of opening it. */
.dated-opening {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: var(--2m-ink-soft);
  font-size: 14px;
}

/* The way out, at the foot of the Twin tab. Rose, and the only filled button on
   any screen that is not the accent: everything else here is an invitation, and
   this one is not. */
.danger-button {
  background: var(--2m-danger);
  color: #fff;
}

/* The second tap's way back. A button rather than a link, because it is the safe
   half of a pair and has to look like the pair's other half: quiet, and beside
   something loud. */
.quiet-button {
  margin-top: var(--2m-space-1);
  border: 1px solid var(--2m-ink);
  background: transparent;
  color: var(--2m-ink);
}

/* The confirmation: what goes, listed, under a rule that separates it from the
   button that opened it. */
.confirm {
  margin-top: var(--2m-space-2);
  border-top: 1px solid var(--2m-line);
  padding-top: var(--2m-space-2);
}

.card .going-heading {
  font-weight: 500;
}

.going {
  margin: 0 0 var(--2m-space-2);
  padding-left: 22px;
  font-size: 14px;
}

.going li + li {
  margin-top: 6px;
}

/* "Level N reached", over the whole screen, once when a Level was gained since
   the last visit. The confetti is a canvas behind the words. */
.level-reached {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.94);
  text-align: center;
}

.level-reached canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.level-reached-words {
  position: relative;
}

.level-reached-number {
  margin: 0;
  /* The darker orange: the accent itself is too faint to read on white. */
  color: var(--2m-active);
  font-size: 48px;
  font-weight: 500;
  line-height: 1.5;
}

.level-reached-verb {
  margin: 0;
  color: var(--2m-black);
  font-size: 18px;
  font-weight: 500;
}

/* The two long waits: the Twin being read, and a Virtual Date being written.
   Three dots that swell and fade one after another, so a page that is working
   does not look the same as one that is stuck. No picture and no library: three
   empty spans and one set of keyframes. */
.waiting {
  display: flex;
  gap: var(--2m-space-1);
  margin: var(--2m-space-2) 0 var(--2m-space-3);
}

.screen-head .waiting {
  margin-bottom: 0;
}

.waiting span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--2m-primary);
  animation: waiting-pulse 1.4s ease-in-out infinite both;
}

.waiting span:nth-child(2) {
  animation-delay: 0.2s;
}

.waiting span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes waiting-pulse {
  0%,
  80%,
  100% {
    opacity: 0.25;
    transform: scale(0.7);
  }

  40% {
    opacity: 1;
    transform: scale(1);
  }
}

/* The loading screen a private link opens on (#113): nothing but the dots, in
   the middle of the screen, and the line or the error under them if there is
   one. */
#loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  text-align: center;
}

#loading .waiting {
  margin: 0;
}

/* With less motion asked for, the three dots stay on screen and stop moving:
   lighter to darker, so they still read as "wait" and not as a row of
   bullets. */
@media (prefers-reduced-motion: reduce) {
  .waiting span {
    animation: none;
    opacity: 0.35;
  }

  .waiting span:nth-child(2) {
    opacity: 0.65;
  }

  .waiting span:nth-child(3) {
    opacity: 1;
  }
}

/* The words still say it. Only the falling paper goes. */
@media (prefers-reduced-motion: reduce) {
  .level-reached canvas {
    display: none;
  }
}

/* The Terms and the Privacy notice (/terms, /privacy), generated from
   docs/legal/ by `pnpm run legal`. Long prose on a phone: room between
   sections, and tables that scroll sideways rather than squeeze. */
.legal h1 {
  font-size: 28px;
  line-height: 1.2;
}

.legal h2 {
  margin-top: var(--2m-space-4);
  font-size: 21px;
}

.legal h3 {
  margin-top: var(--2m-space-3);
  font-size: 17px;
}

.legal blockquote {
  margin: var(--2m-space-2) 0;
  padding: var(--2m-space-2);
  border-radius: var(--2m-radius-control);
  background: var(--2m-lotus-white);
}

.legal blockquote p {
  margin: 0;
}

.legal table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
}

.legal th,
.legal td {
  padding: var(--2m-space-1);
  border: 1px solid var(--2m-line);
  text-align: left;
  vertical-align: top;
}
