/* OpenPet's stylesheet.

   Tokens first: sand for the pet's body, a colour per source, sun and blush
   for the fire eyes, ink-2 and ink-3 for the two quiets below the ink. Flat
   fills only — there are no gradients, apart from the one fade that lets the
   thread scroll under the composer. */
:root {
  /* The untinted canvas and card. The mood layer washes --canvas and --white
     with a little of the night's colour and derives them from these two, so
     the base values have one home and the tint has one source. */
  --canvas-0: #f8f4ec;
  --white-0: #fffdf8;
  --canvas: var(--canvas-0);
  --white: var(--white-0);
  --sand: #e8dfd1;
  --sand-2: #f1eae0;

  --ink: #122039;
  --ink-2: #5b6577;
  --ink-3: #8c93a1;
  --line-0: #e6ddcf;
  --line: var(--line-0);

  /* One colour per source, and the soft wash each one's tag sits on. */
  --sage: #6f8f7c;   --sage-soft: #e4ece6;
  --mauve: #a79bad;  --mauve-soft: #eeeaf0;
  --sky: #91a9cc;    --sky-soft: #e7ecf4;
  --slate: #5e6b7d;
  --sun: #e7b96d;    --sun-soft: #f7ecd6;
  --blush: #d99582;  --blush-soft: #f6e4dd;  --blush-ink: #b9694f;
  --spirit: #d9b77c;

  /* The chat panel's own green. */
  --chat: #e8f1eb;
  --chat-line: #c5dacb;
  --emerald: #2e7a5e;
  --forest: #1f4d3c;
  --moss: #557566;

  --serif: ui-serif, "New York", "Iowan Old Style", Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --rise: cubic-bezier(.2, .8, .2, 1);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--canvas); color: var(--ink);
  font: 17px/1.45 var(--sans);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
}
button { font: inherit; color: inherit; background: none; border: 0; padding: 0;
  cursor: pointer; touch-action: manipulation; text-align: left; }
:is(button, a, summary, input):focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 10px; }

h1, h2, h3 { font-family: var(--serif); font-weight: 500; text-wrap: balance; margin: 0; color: var(--ink); }
h1 { font-size: 34px; line-height: 1.12; letter-spacing: -.01em; }
h2 { font-size: 24px; line-height: 1.2; }
h3 { font-size: 18px; }
p { margin: 0; }
a { color: var(--ink); }
[hidden] { display: none !important; }

/* ── Text roles ─────────────────────────────────────────────────────────── */

.lead { font-family: var(--serif); font-size: 21px; line-height: 1.25; font-weight: 400; text-wrap: balance; }
.sub { color: var(--ink-2); font-size: 15px; }
.meta { color: var(--ink-3); font-size: 13px; line-height: 1.35; font-variant-numeric: tabular-nums; }
.date { color: var(--ink-2); font-size: 15px; }
.name { font-weight: 600; font-size: 16px; }
.kicker { font-family: var(--sans); font-size: 12px; line-height: 1.45; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 600; margin: 8px 4px -8px; }
.thread > .kicker { margin-bottom: -4px; }

/* A chip that opens a small note beneath it: the streak and its source. */
.pop { position: relative; flex: none; }
.pop > summary { list-style: none; cursor: pointer; }
.pop > summary::-webkit-details-marker { display: none; }
.pop .menu { min-width: 0; width: max-content; max-width: 260px; }
.pop .mhow { padding: 8px 10px; }
.type { display: inline-flex; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); }
.next { font-size: 13px; color: var(--ink-2); padding: 0 4px; }

/* ── The screen ─────────────────────────────────────────────────────────────

   One column, the width of a phone. 390 is the width it is laid out for and
   430 the widest phone it has to hold; on a desktop it stays a column rather
   than stretching into a page. Siblings are spaced by the column's gap, never
   by their own margins. */
.wrap { max-width: 430px; min-height: 100dvh; margin: 0 auto; display: flex; flex-direction: column;
  gap: 16px; padding: calc(18px + env(safe-area-inset-top)) 20px calc(24px + env(safe-area-inset-bottom)); }
/* Room at the foot for the section bar, so the last thing on the screen is
   reachable rather than parked behind it. */
.wrap.has-tabs { padding-bottom: calc(92px + env(safe-area-inset-bottom)); }
.grow { flex: 1; min-width: 0; }
/* The welcome's space above the pet: a quarter of the space below. */
.grow--top { flex: .25; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.stack { display: flex; flex-direction: column; gap: 4px; }
.left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.center { text-align: center; align-items: center; }

/* ── Surfaces ───────────────────────────────────────────────────────────────

   A card is one thing: white on the canvas, one hairline, 20px corners. A
   list is several things of one kind in a single card, divided by hairlines —
   never by gaps, because a gap between two rows reads as two objects. */
.card { background: var(--white); border: 1px solid var(--line); border-radius: 20px;
  padding: 16px 18px 6px; display: flex; flex-direction: column; gap: 10px; }
.card.pad { padding-bottom: 16px; }
.list { background: var(--white); border: 1px solid var(--line); border-radius: 20px; padding: 0 16px; }
.srow { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 0; min-height: 64px; width: 100%; color: inherit; text-decoration: none; }
.srow + .srow { border-top: 1px solid var(--line); }
.srow .meta { font-size: 12px; }
a.srow:hover .name { text-decoration: underline; text-underline-offset: 3px; }
.chev { width: 18px; height: 18px; flex: none; fill: none; stroke: var(--ink-3); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }

/* A row that is a way somewhere else: Add a source. Its name says where, and
   its line says what is there. */
.rowcard { display: flex; align-items: center; gap: 12px; width: 100%; background: var(--white);
  border: 1px solid var(--line); border-radius: 20px; padding: 12px 16px; min-height: 60px;
  color: inherit; text-decoration: none; }
.rowcard .grow { display: flex; flex-direction: column; }
.rowcard.addsrc .name { color: var(--ink-2); }
a.rowcard:hover .name { text-decoration: underline; text-underline-offset: 3px; }

/* What the pet says instead of a card it has taken away. */
.noted { background: var(--sand-2); border-radius: 16px; padding: 14px 16px; font-size: 15px; color: var(--ink-2); }

/* A reason and a next action, where something needs the person's attention. */
.notice { background: var(--sun-soft); border: 1px solid var(--sun); border-radius: 16px;
  padding: 14px 16px; font-size: 15px; color: var(--ink); display: flex; flex-direction: column; gap: 8px; }
.notice strong { font-weight: 600; }
.notice .btn { margin-top: 4px; }

/* What someone will need before they go anywhere: a card of plain points. */
.needs { background: var(--white); border: 1px solid var(--line); border-radius: 20px;
  padding: 16px 18px; font-size: 15px; display: flex; flex-direction: column; gap: 8px; }
.needs > strong { font-size: 13px; font-weight: 400; color: var(--ink-3); }
.needs ul, .needs ol { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 6px; }

/* ── Controls ───────────────────────────────────────────────────────────── */

.btns { display: flex; flex-direction: column; gap: 8px; }
.btns .built { display: flex; align-items: center; justify-content: center; gap: 6px;
  padding-top: 6px; color: var(--ink-3); text-decoration: none; }
.btns .built:hover { color: var(--ink-2); }
.wordmark { height: 13px; width: auto; display: block; }
.btn { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 54px;
  padding: 0 20px; border: 0; border-radius: 16px; background: var(--ink); color: var(--white);
  font: 600 17px/1.2 var(--sans); text-align: center; text-decoration: none; cursor: pointer; }
.btn:hover { background: #1b2c4b; }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
/* The secondary is words, not a box: "Not now", "Start again", "Keep". */
.btn--quiet, .btn.ghost { background: none; color: var(--ink-2); min-height: 48px; font-weight: 500; }
.btn--quiet:hover, .btn.ghost:hover { background: none; color: var(--ink); }
/* A secondary that is still a place to go gets the sand box. */
.btn--soft { background: var(--sand-2); color: var(--ink); }
.btn--soft:hover { background: var(--sand); }
/* Taking something away is a real action and gets a real button, in the one
   colour this stylesheet keeps for it. */
.btn--danger { background: var(--blush); color: #fff; }
.btn--danger:hover { background: #cf8475; }

.chip { display: inline-flex; align-items: center; gap: 6px; min-height: 30px; padding: 0 12px;
  border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--sand-2); color: var(--ink-2);
  white-space: nowrap; text-decoration: none; }
.chip svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }
.chip .i-check { stroke: var(--sage); stroke-width: 2.4; }
a.chip:hover, button.chip:hover { background: var(--sand); color: var(--ink); }
.chip--nav { min-height: 36px; }

/* A text control inside a sentence or at the end of a row. */
.inline { appearance: none; background: none; border: 0; padding: 0 2px; cursor: pointer;
  font: 600 15px/1 var(--sans); color: var(--ink); text-decoration: underline;
  text-underline-offset: 3px; text-decoration-thickness: 1px; min-height: 44px;
  display: inline-flex; align-items: center; white-space: nowrap; flex: none; }
.inline:hover { color: var(--ink-2); }

/* Beta reads as a quiet pill beside a name, and a correction as a warmer one
   beside what was corrected. */
.beta, .tagc { display: inline-flex; font-size: 11px; font-weight: 600; color: var(--ink-2);
  background: var(--sand-2); border-radius: 6px; padding: 1px 7px; margin-left: 6px;
  vertical-align: 1px; white-space: nowrap; font-family: var(--sans); }
.tagc { color: var(--blush-ink); background: var(--blush-soft); }

/* A single text field. 16px is not a taste: anything smaller makes iOS Safari
   zoom the page on focus and never zoom back. */
input.text { appearance: none; width: 100%; min-height: 52px; padding: 0 16px; border-radius: 14px;
  border: 1px solid var(--line); background: var(--white); font: 16px/1.4 var(--sans); color: var(--ink);
  outline: none; }
input.text::placeholder { color: var(--ink-3); }
input.text:focus-visible { border-color: var(--ink-2); outline: none; }

/* ── The ⋯ menu ─────────────────────────────────────────────────────────────

   Every reading, source and entry keeps its controls behind one ⋯. It is a
   <details>, so it opens and closes without a script; openpet.js only closes
   the others, closes on Escape and outside taps, and opens upward near the
   foot of the screen. */
.more { position: relative; flex: none; margin-right: -12px; }
.more > summary { list-style: none; width: 44px; height: 44px; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 12px; color: var(--ink-3); cursor: pointer; }
.more > summary::-webkit-details-marker { display: none; }
.more > summary:hover, .more[open] > summary { background: var(--sand-2); color: var(--ink); }
.more .i-dots { width: 20px; height: 20px; fill: currentColor; }
.menu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 30; min-width: 200px;
  background: var(--white); border: 1px solid var(--line); border-radius: 16px; padding: 6px;
  box-shadow: 0 10px 30px rgba(18, 32, 57, .12); display: flex; flex-direction: column; text-align: left; }
.more.up .menu { top: auto; bottom: calc(100% + 6px); }
.menu .mcap { font-size: 12px; color: var(--ink-3); padding: 8px 12px 4px; }
.menu .mhow { font-size: 13px; line-height: 1.4; color: var(--ink-2); padding: 2px 12px 8px; max-width: 260px; }
.menu > :is(button, a) { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
  padding: 0 12px; border-radius: 10px; font: 500 15px/1.2 var(--sans); color: var(--ink);
  text-decoration: none; white-space: nowrap; }
.menu > :is(button, a):hover { background: var(--sand-2); }
.menu .warn, .menu .wrong { color: var(--blush-ink); }
.menu svg { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round; }
/* Whatever holds an open menu rises above its neighbours, so the menu is never
   drawn under the next card down. */
:is(.card, .list, .srow, .hero, .chatbox, .you, .receipt, .goal-top, .ghead):has(> .more[open], .more[open]) {
  position: relative; z-index: 20; }

/* ── The section bar ────────────────────────────────────────────────────────

   Three tabs, on the canvas, frosted so the page scrolls visibly beneath it.
   It stays inside the column: the app is a phone screen, and a bar the width
   of a desktop window makes it look like a broken page. */
.tabs { position: fixed; left: 0; right: 0; bottom: 0; z-index: 5; max-width: 430px; margin: 0 auto;
  display: flex; justify-content: space-around; padding: 6px 16px calc(8px + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--canvas) 94%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); border-top: 1px solid var(--line); }
/* Direct children only: the label inside a tab is a span too. */
.tabs > a { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-width: 72px; min-height: 50px; font-size: 11px; font-weight: 600; color: var(--ink);
  opacity: .4; text-decoration: none; }
.tabs > a[aria-current="page"] { opacity: 1; }
.tabs > a:hover { opacity: .7; }
.tabs > a[aria-current="page"]:hover { opacity: 1; }
.tabs svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round; }

/* ── The pet ────────────────────────────────────────────────────────────── */

.hero { display: flex; flex-direction: column; align-items: center; text-align: center; }
.hero.compact { flex-direction: row; gap: 14px; text-align: left; align-items: center; }
.hero.compact .stack { gap: 4px; }

.pet { display: block; overflow: visible; }
.pet .core { fill: var(--sand); }
.pet .eye { fill: var(--ink); }
.pet .eye-closed { fill: none; stroke: var(--ink); stroke-width: 3.2; stroke-linecap: round; }
.pet .mouth { fill: var(--ink); }
.pet .mouth-line { fill: none; stroke: var(--ink); stroke-width: 3.2; stroke-linecap: round; }
.pet .flame-outer { fill: var(--sun); }
.pet .flame-core { fill: var(--blush); }

/* The stage: the orbit's far half, the pet, the orbit's near half, then the
   dots, each travelling between the two by z-index as it comes round. */
.stage { position: relative; isolation: isolate; flex: none; margin: 0 auto; }
.stage > svg { position: absolute; left: 0; top: 0; width: 100%; overflow: visible; }
.stage .ringb { z-index: 0; height: 100%; }
.stage .pet { z-index: 1; height: auto; }
.stage .ringf { z-index: 2; height: 100%; }
.stage .arc { fill: none; stroke: var(--sand); stroke-width: 1.3; stroke-dasharray: 1.5 5; stroke-linecap: round; }

.orb { position: absolute; left: 0; top: 0; height: 24px; display: inline-flex; align-items: center; gap: 0;
  padding: 0; border-radius: 999px; transform-origin: 12px 12px;
  transform: translate(-12px, -12px) scale(var(--s, 1)); opacity: var(--o, 1); white-space: nowrap;
  transition: background-color .18s ease, box-shadow .18s ease, gap .18s ease, padding .18s ease; }
.orb.r { flex-direction: row-reverse; transform-origin: calc(100% - 12px) 12px;
  transform: translate(calc(-100% + 12px), -12px) scale(var(--s, 1)); }
.orb-dot { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; color: #fff; flex: none; }
.orb-dot svg { width: 13px; height: 13px; }
.orb--sage { background: var(--sage); } .orb--mauve { background: var(--mauve); }
.orb--sky { background: var(--sky); } .orb--sun { background: var(--sun); }
.orb--slate { background: var(--slate); } .orb--blush { background: var(--blush); }
.orb--spirit { background: var(--spirit); }
.orb .lab { display: flex; flex-direction: column; max-width: 0; overflow: hidden; opacity: 0; text-align: left;
  line-height: 1.15; transition: max-width .22s ease, opacity .18s ease; }
.orb .lab b { font-size: 13px; font-weight: 600; color: var(--ink); }
.orb .lab small { font-size: 11px; color: var(--ink-3); }
/* Open: tapped, or hovered where there is a pointer to hover with. */
.orb.open { height: auto; transform: translate(-16px, -18px); z-index: 9 !important; --s: 1 !important; --o: 1 !important;
  background: var(--white); box-shadow: 0 0 0 1px var(--line), 0 8px 20px rgba(18, 32, 57, .10);
  gap: 8px; padding: 4px 12px 4px 4px; }
.orb.r.open { transform: translate(calc(-100% + 16px), -18px); padding: 4px 4px 4px 12px; }
.orb.open .lab { max-width: 210px; opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  .orb:hover, .orb:focus-visible { height: auto; transform: translate(-16px, -18px); z-index: 9 !important;
    --s: 1 !important; --o: 1 !important; background: var(--white);
    box-shadow: 0 0 0 1px var(--line), 0 8px 20px rgba(18, 32, 57, .10); gap: 8px; padding: 4px 12px 4px 4px; }
  .orb.r:hover, .orb.r:focus-visible { padding: 4px 4px 4px 12px; transform: translate(calc(-100% + 16px), -18px); }
  .orb:hover .lab, .orb:focus-visible .lab { max-width: 210px; opacity: 1; }
}
/* The consent screen's pet wears its one source as a small dot. */
.stage.mini .orb { height: 12px; pointer-events: none; transform: translate(-6px, -6px) scale(var(--s, 1));
  transform-origin: 6px 6px; }
.stage.mini .orb-dot { width: 12px; height: 12px; }

/* The number, what it is, and where it came from. */
.signal { display: flex; flex-direction: column; align-items: center; width: 100%; }
.score b { display: block; font-size: 60px; line-height: 1; font-weight: 600; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; margin-top: 6px; color: var(--ink); }
.signal .lbl { font-size: 15px; color: var(--ink); font-weight: 500; margin-top: 6px; }
.signal .srcline { justify-content: center; margin: 0; }
.signal .srcline .meta { flex: none; }
.signal .noted, .signal .state-line { margin-top: 12px; text-align: left; }

/* The pet small and still: the avatar beside its own words. */
.mini .mini-core { fill: var(--sand); }
.mini .mini-eye { fill: var(--ink); }
.mini .mini-mouth { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; }
.av { display: flex; flex: none; }
.av svg { width: 100%; height: 100%; }

/* ── The thread ─────────────────────────────────────────────────────────────

   One card per reading: a serif headline, then its source line — the source's
   colour, where it came from, and the ⋯ that holds "That's wrong". */
.thread { display: flex; flex-direction: column; gap: 12px; }
.observation .claim { font-family: var(--serif); font-size: 21px; line-height: 1.25; font-weight: 500;
  letter-spacing: -.005em; text-wrap: balance; }
.srcline { display: flex; align-items: center; gap: 8px; min-height: 44px; margin-top: -2px; }
.srcline .meta, .srcline .provenance { flex: 1; min-width: 0; }
.provenance { color: var(--ink-3); font-size: 13px; line-height: 1.35; font-variant-numeric: tabular-nums; }
.provenance span + span::before { content: " · "; }
.sw { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--ink-3); }
.sw--sage { background: var(--sage); } .sw--mauve { background: var(--mauve); } .sw--sky { background: var(--sky); }
.sw--sun { background: var(--sun); } .sw--slate { background: var(--slate); } .sw--blush { background: var(--blush); }
.link { width: 22px; height: 14px; flex: none; }
.link circle { fill: none; stroke-width: 2; stroke: var(--ink-3); }
.link .ring--sage { stroke: var(--sage); } .link .ring--mauve { stroke: var(--mauve); }
.link .ring--sky { stroke: var(--sky); } .link .ring--sun { stroke: var(--sun); }

/* A claim's figure: the days it was drawn from, the ones it is about in the
   source's colour and the rest in sand. Bars for values, dots for days. */
.chart { display: block; width: 100%; height: auto; overflow: visible; }
.chart text { font-family: var(--sans); font-size: 10px; fill: var(--ink-3); font-variant-numeric: tabular-nums; }
.chart .val { font-size: 11px; font-weight: 700; fill: var(--ink); }
.chart rect { fill: var(--sand); }
.chart circle { fill: var(--sand-2); }
.chart circle.none { fill: var(--sand); }
.fig--sage .on { fill: var(--sage); } .fig--mauve .on { fill: var(--mauve); }
.fig--sky .on { fill: var(--sky); } .fig--sun .on { fill: var(--sun); }
.fig--blush .on { fill: var(--blush); }
.ends { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-3);
  margin-top: -4px; font-variant-numeric: tabular-nums; }

/* A state's sentence and where to go about it. Loose on the screen it is its
   own quiet surface; inside a card it keeps only a rule above it. The two
   states that are warnings keep their colour. */
.state-line { display: block; padding: 14px 16px; background: var(--sand-2); border-radius: 16px;
  font-size: 15px; line-height: 1.5; color: var(--ink-2); }
.state-line .state { display: inline; }
.state-line .next { display: inline-block; margin-top: 6px; padding: 0; font-size: 15px; font-weight: 600;
  color: var(--ink); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.state-line:has(.state--stale), .state-line:has(.state--partial) { background: var(--sun-soft); }
.state-line:has(.state--revoked) { background: var(--blush-soft); }
.card .state-line { background: none; border-top: 1px solid var(--line); border-radius: 0; padding: 12px 0 10px; }

/* ── Talk to your pet ───────────────────────────────────────────────────────

   One tinted surface for the pet's question, the replies, the day's
   conversation and the composer. It is last on the screen because it is the
   one thing here that writes rather than reads. */
.chatbox { background: var(--chat); border: 1px solid var(--chat-line); border-radius: 24px;
  padding: 14px 14px 0; display: flex; flex-direction: column; gap: 12px; margin-top: 4px;
  scroll-margin-bottom: calc(80px + env(safe-area-inset-bottom)); }
.chat-head { display: flex; align-items: center; gap: 10px; padding: 2px 2px 10px; border-bottom: 1px solid var(--chat-line); }
.chat-head .av { width: 32px; height: 32px; }
.chat-head .grow { display: flex; flex-direction: column; line-height: 1.25; }
.chat-head b { font-size: 15px; color: var(--forest); }
.chat-head small { font-size: 12px; color: var(--moss); }

.say { display: flex; gap: 10px; align-items: flex-start; padding: 4px 0; }
.say .av { width: 30px; height: 30px; margin-top: 1px; }
.say .stack { gap: 6px; }
.say p { font-family: var(--serif); font-size: 19px; line-height: 1.35; text-wrap: balance; }
.you { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.you > p { background: var(--ink); color: var(--white); border-radius: 20px 20px 6px 20px;
  padding: 10px 15px; max-width: 80%; font-size: 16px; overflow-wrap: anywhere; }
.chatbox .you > p { background: var(--forest); }
.receipt { display: flex; align-items: center; gap: 2px; }
.receipt .chip { background: transparent; padding: 0 4px; font-weight: 500; color: var(--ink-3); white-space: normal; }
.receipt .more { margin-right: -8px; }
.replies { display: flex; flex-wrap: wrap; gap: 8px; padding-left: 40px; }
.replies form { display: contents; }
.qchip { display: inline-flex; align-items: center; flex: none; min-height: 36px; padding: 0 14px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--white); font-size: 14px;
  font-weight: 500; color: var(--ink-2); text-decoration: none; cursor: pointer; }
.chatbox .qchip { border-color: var(--chat-line); color: var(--forest); }
.chatbox .qchip:hover { border-color: var(--emerald); }
.chatbox .noted { background: rgba(255, 253, 248, .7); }

/* An entry being corrected, in place. */
.edit-entry { display: flex; flex-direction: column; gap: 8px; }
.edit-entry .row { justify-content: flex-end; gap: 16px; }
.edit-entry .btn { width: auto; min-height: 44px; padding: 0 18px; font-size: 15px; background: var(--emerald); }

/* The composer rides the foot of the screen while the panel is in view, and
   the conversation scrolls under it through a short fade of the panel's own
   colour. The kind chips only show while the box is in use or one is chosen. */
.composer { position: sticky; bottom: calc(65px + env(safe-area-inset-bottom)); z-index: 2;
  margin: 0 -14px; padding: 10px 14px 14px; display: flex; flex-direction: column; gap: 8px;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--chat) 0%, transparent), var(--chat) 12px);
  border-radius: 0 0 23px 23px; }
.chips { display: none; gap: 8px; overflow-x: auto; scrollbar-width: none; margin: 0 -14px; padding: 0 14px; }
.chips::-webkit-scrollbar { display: none; }
.composer:focus-within .chips, .composer:has(.chip-choice input:checked) .chips { display: flex; }
.chip-choice { display: inline-flex; flex: none; }
/* The radio is out of sight but not out of the accessibility tree or the tab
   order, so the group still works from a keyboard. */
.chip-choice input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); }
.chip-choice span { display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px;
  border-radius: 999px; border: 1px solid var(--chat-line); background: var(--white); font-size: 14px;
  font-weight: 500; color: var(--forest); cursor: pointer; }
.chip-choice input:checked + span { background: var(--forest); border-color: var(--forest); color: var(--white); }
.chip-choice input:focus-visible + span { outline: 2px solid var(--emerald); outline-offset: 2px; }
.field { display: flex; align-items: center; gap: 6px; background: var(--white); border: 1px solid var(--chat-line);
  border-radius: 26px; padding: 4px 4px 4px 18px; box-shadow: 0 1px 0 rgba(31, 77, 60, .04); }
.field:focus-within { border-color: var(--emerald); box-shadow: 0 0 0 3px rgba(46, 122, 94, .14); }
.input { flex: 1; min-width: 0; min-height: 44px; border: 0; background: none; color: var(--ink);
  font: 16px/1.4 var(--sans); outline: none; }
.input::placeholder { color: var(--ink-3); }
.input:focus-visible { outline: none; }
.send { width: 44px; height: 44px; border-radius: 50%; background: var(--emerald); color: var(--white);
  display: flex; align-items: center; justify-content: center; flex: none; }
.send svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round; }
.send:focus-visible { outline-offset: 2px; border-radius: 50%; }

/* The way back to the panel when it is scrolled out of sight. Shown by
   openpet.js, which is the only thing that can tell it is out of sight. */
.chatjump { display: none; position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(76px + env(safe-area-inset-bottom)); z-index: 4; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 18px 0 8px; border-radius: 999px; background: var(--emerald); color: #fff;
  font-size: 14px; font-weight: 600; text-decoration: none; box-shadow: 0 8px 24px rgba(31, 77, 60, .22); }
.chatjump .av { width: 28px; height: 28px; }
.chat-away .chatjump { display: inline-flex; }

/* ── Goals ──────────────────────────────────────────────────────────────── */

.ghead { position: sticky; top: 0; z-index: 4; background: var(--canvas);
  margin: calc(-18px - env(safe-area-inset-top)) -20px 0;
  padding: calc(18px + env(safe-area-inset-top)) 20px 10px; border-bottom: 1px solid transparent;
  transition: border-color .2s ease; }
.ghead.stuck { border-bottom-color: var(--line); }
.goal-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.dom { display: inline-flex; align-items: center; font-size: 11px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; padding: 3px 8px; border-radius: 6px; }
.dom--body { background: #e4ece6; color: #56735f; }
.dom--mind { background: #eeeaf0; color: #7a6d83; }
.dom--spirit { background: #f7ecd6; color: #8f6a2c; }
.watch { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-3); font-weight: 500; }
.goal .name { margin-top: -2px; }
.goal .claim, .goal .head { font-family: var(--serif); font-size: 21px; line-height: 1.25; font-weight: 500;
  letter-spacing: -.005em; text-wrap: balance; }
.goal-reading { display: flex; flex-direction: column; gap: 10px; }
.goal form { display: flex; }
.sw--spirit { background: var(--spirit); } .fig--spirit .on { fill: var(--spirit); }
.goal-add { display: none; }
.goal-add:target, .goal-add.open { display: flex; }
.wrap:has(.goal-add:target) .add-chip { display: none; }
.wrap:not(:has(.goal-add:target)) .cancel-chip { display: none; }
.addrow { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 56px;
  border: 1.5px dashed var(--sand); border-radius: 20px; color: var(--ink-2); font-weight: 600; font-size: 15px;
  text-decoration: none; }
.addrow:hover { border-color: var(--ink-3); color: var(--ink); }

/* ── Sources ────────────────────────────────────────────────────────────── */

.mark { width: 36px; height: 36px; flex: none; display: flex; align-items: center; justify-content: center; }
.mark > svg { width: 36px; height: 36px; display: block; overflow: visible; }
.badge { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--sand-2); color: var(--ink-3); }
.badge svg { width: 16px; height: 16px; }
.badge--sage { background: color-mix(in srgb, var(--sage) 22%, var(--white)); color: var(--sage); }
.badge--mauve { background: color-mix(in srgb, var(--mauve) 22%, var(--white)); color: var(--mauve); }
.badge--sky { background: color-mix(in srgb, var(--sky) 22%, var(--white)); color: var(--sky); }
.badge--sun { background: color-mix(in srgb, var(--sun) 22%, var(--white)); color: var(--sun); }
.badge--slate { background: color-mix(in srgb, var(--slate) 22%, var(--white)); color: var(--slate); }
.badge--blush { background: color-mix(in srgb, var(--blush) 22%, var(--white)); color: var(--blush); }
.badge--spirit { background: color-mix(in srgb, var(--spirit) 22%, var(--white)); color: var(--spirit); }

/* A source on its way: greyed, and with nothing to press. */
.srow.soon { opacity: .5; }

/* A source waiting on a computer: on the orbit already, hollow until read. */
.orb.wait .orb-dot { background: var(--white) !important; color: var(--ink-3); box-shadow: inset 0 0 0 1.5px var(--sand); }

/* Which account this is, at the foot of Sources. Quiet enough to ignore,
   legible enough to read down a phone line when something has gone wrong. */
.account { text-align: center; font-size: 12px; line-height: 1.6; color: var(--ink-3); padding: 0 8px; }
.account-id { font-variant-numeric: tabular-nums; word-break: break-all; user-select: all; }

/* Delete everything asks once, in place, without a dialog. */
.confirm-del > summary { list-style: none; }
.confirm-del > summary::-webkit-details-marker { display: none; }
.confirm-del[open] > summary { display: none; }
.confirm-del .confirm { display: flex; flex-direction: column; gap: 8px; }
.confirm-del .confirm .sub { color: var(--ink); }

/* ── Logs and what the pet remembers ────────────────────────────────────── */

.entry-text { font-size: 16px; overflow-wrap: anywhere; }

/* ── Onboarding ─────────────────────────────────────────────────────────── */

/* Start with one source: each kind's tag and plain line, then its sources. */
.kind { display: flex; flex-direction: column; gap: 10px; }
.kind-head { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 0 4px; }
.kind-head .meta { font-size: 14px; color: var(--ink-2); }
/* The one quiet line at the foot of a screen. */
.foot { text-align: center; font-size: 13px; max-width: 300px; margin: 0 auto; }

/* The pet thinking while Vana is approved: three dots, nothing more. */
.typing { display: inline-flex; gap: 4px; padding: 10px 0; }
.typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-3); }

/* The one row being read. Its bar fills when the read has finished, and not
   before: it is a state, not a timer. */
.bar-t { height: 4px; border-radius: 2px; background: var(--sand-2); overflow: hidden; margin-top: 6px; }
.bar-t span { display: block; height: 100%; border-radius: 2px; width: 0; transition: width .6s var(--rise); }
.bar-t span.full { width: 100%; }
.bar--sage { background: var(--sage); } .bar--mauve { background: var(--mauve); }
.bar--sky { background: var(--sky); } .bar--sun { background: var(--sun); }
.bar--spirit { background: var(--spirit); } .bar--blush { background: var(--blush); }

/* The connect screen's two moments: the ask, and once Vana is open, only that.
   The buttons keep their place while hidden, so the pet does not jump. */
.then { display: none; }
.to-vana .ask.stack { display: none; }
.to-vana .then { display: flex; }
.to-vana .btns.ask { visibility: hidden; }

/* Asking how a request is getting on, without saying so. Read by assistive
   technology, never seen; a reason that has to be seen is let out. */
.quiet-poll { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

h1 .beta { vertical-align: 6px; }
/* A way elsewhere that is not the next step: small, quiet, at the foot. */
.aside { text-align: center; font-size: 13px; color: var(--ink-3); padding: 4px 0; }
.aside a { color: var(--ink-2); text-underline-offset: 3px; }
.aside a:hover { color: var(--ink); }
.foot { margin-top: auto; }
.foot .built { display: flex; align-items: center; justify-content: center; gap: 6px;
  margin-bottom: 10px; color: var(--ink-3); text-decoration: none; }
.foot .built:hover { color: var(--ink-2); }
.install .row { justify-content: flex-start; }
.install .btn { width: auto; }
code { font-size: 14px; overflow-wrap: anywhere; }

/* ── Arrival ────────────────────────────────────────────────────────────────

   Each block rises into place once, when a screen is opened. After an action
   swaps the page in place, nothing moves again: openpet.js marks the body
   settled, and only a fresh screen plays it. */
@media (prefers-reduced-motion: no-preference) {
  /* The pet taking in a source just read: a small bounce, and the new dot
     arriving on its orbit. Scale rather than transform, which the drift owns. */
  .reacted > .stage > .pet { animation: take-in .7s cubic-bezier(.3, 1.4, .5, 1); }
  .reacted .orb.join .orb-dot { animation: join .6s cubic-bezier(.3, 1.5, .5, 1) backwards; }
  .typing i { animation: dots 1s infinite; }
  .typing i:nth-child(2) { animation-delay: .15s; } .typing i:nth-child(3) { animation-delay: .3s; }
  .wrap > * { animation: rise .6s var(--rise) backwards; }
  /* A figure draws itself in: bars grow from the baseline, dots arrive. */
  .chart rect { transform-box: fill-box; transform-origin: bottom; animation: growy .7s var(--rise) .2s backwards; }
  .chart circle { transform-box: fill-box; transform-origin: center; animation: pop .5s cubic-bezier(.3, 1.5, .5, 1) .2s backwards; }
  .settled .chart rect, .settled .chart circle { animation: none; }
  .wrap > :nth-child(2) { animation-delay: .05s; } .wrap > :nth-child(3) { animation-delay: .1s; }
  .wrap > :nth-child(4) { animation-delay: .15s; } .wrap > :nth-child(5) { animation-delay: .2s; }
  .wrap > :nth-child(6) { animation-delay: .25s; } .wrap > :nth-child(n+7) { animation-delay: .3s; }
  .settled .wrap > * { animation: none; }
  details.more[open] .menu { animation: menu .16s ease-out; }
}
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes dots { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@keyframes take-in { 0%, 100% { scale: 1; } 30% { scale: 1.07 .93; } 55% { scale: .96 1.05; } 80% { scale: 1.02 .99; } }
@keyframes join { from { opacity: 0; scale: 0; } to { opacity: 1; scale: 1; } }
@keyframes growy { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes pop { from { opacity: 0; transform: scale(.2); } to { opacity: 1; transform: scale(1); } }
@keyframes menu { from { opacity: 0; transform: scale(.96) translateY(-4px); } to { opacity: 1; transform: none; } }

/* ── Motion for the face ────────────────────────────────────────────────────

   Every rule is keyed on the classes pet.html uses; nothing here changes a
   fill, a coordinate, or a colour source. The body's change of shape is the
   drawing's own <animate>; everything else is here.

   Each element owns one transform property per loop — `rotate`, `translate`
   or `scale` — so two loops on one element never fight:
     .body   rotate = sway        translate = sag | bob
     .core   scale  = breath
     .face   translate.y = follows the breath
     .eyes   translate.x = glance
     .eye    scale  = blink
     .mouth* scale  = breath / grin
   The flames sit inside groups that place them, so their own flicker and lick
   never fight the placing.

   --pet-tempo is the pet's pace. Every duration above is MULTIPLIED by it,
   so a smaller number is a shorter cycle and therefore a faster pet; the
   base durations are drawn slow on purpose, and the mood layer below sets
   the pace. --pet-speed, inherited from any ancestor, multiplies it in turn:
   set 3 for slow motion while working on a keyframe.

   Resting is the SLOWEST look by design: it is the pet asleep. */

.pet--live :is(.body, .core, .face, .eyes, .eye, .eye-closed, .mouth, .mouth-line, .flame-outer, .flame-core) {
  transform-box: fill-box; transform-origin: 50% 50%; }
.pet--live :is(.body, .core, .flame-outer, .flame-core) { transform-origin: 50% 90%; }

/* The common loop. */

.pet--live .body { animation: sway calc(7s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live .core { animation: breathe calc(4.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live .face { animation: face-breathe calc(4.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live .eye  { animation: blink calc(5.2s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live .mouth-line { animation: mouth-breathe calc(4.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }

@keyframes sway          { 0%, 100% { rotate: -1.4deg; } 50% { rotate: 1.4deg; } }
@keyframes breathe       { 0%, 100% { scale: 1 1; } 50% { scale: 1.025 1.035; } }
@keyframes face-breathe  { 0%, 100% { translate: 0 0; } 50% { translate: 0 -1.8px; } }
@keyframes mouth-breathe { 0%, 100% { scale: 1 1; } 50% { scale: 1.03 1; } }
@keyframes blink         { 0%, 91%, 100% { scale: 1 1; } 93% { scale: 1 0.08; } 95.5% { scale: 1 1; } }

/* Resting: asleep; nothing has been read yet. */

.pet--live.pet--resting .body { animation-duration: calc(11s * var(--pet-tempo) * var(--pet-speed, 1)); }
.pet--live.pet--resting .core { animation: breathe-deep calc(6.8s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--resting .face,
.pet--live.pet--resting .mouth-line { animation-duration: calc(6.8s * var(--pet-tempo) * var(--pet-speed, 1)); }
.pet--live.pet--resting .eye-closed { animation: dream calc(9s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--resting .eye-closed + .eye-closed { animation-delay: 0.12s; }

@keyframes breathe-deep { 0%, 100% { scale: 1 1; } 50% { scale: 1.012 1.04; } }
@keyframes dream        { 0%, 93%, 100% { scale: 1 1; } 95.5% { scale: 1 1.55; } 98% { scale: 1 1; } }

/* Tired: under 40. */

.pet--live.pet--tired .body { animation:
  sway calc(9s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite,
  sag  calc(8s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--tired .core { animation: sigh calc(5.6s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--tired .face { animation: face-sigh calc(5.6s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--tired .mouth-line { animation: none; }
/* heavy lids: the dot squashes toward its own base and only slowly lifts */
.pet--live.pet--tired .eye { transform-origin: 50% 100%; animation: heavy-blink calc(7s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }

@keyframes sag         { 0%, 100% { translate: 0 0; } 42%, 52% { translate: 0 2.6px; } 60% { translate: 0 -0.4px; } 66% { translate: 0 0; } }
@keyframes sigh        { 0%, 100% { scale: 1 1; } 34%, 52% { scale: 1.015 1.035; } 68% { scale: 1 1; } }
@keyframes face-sigh   { 0%, 100% { translate: 0 0; } 34%, 52% { translate: 0 -1.8px; } 68% { translate: 0 0; } }
@keyframes heavy-blink { 0%, 38%, 100% { scale: 1 0.62; } 50%, 58% { scale: 1 0.12; } 72% { scale: 1 0.66; } 80% { scale: 1 0.62; } }

/* Okay: 40 to 69. */

.pet--live.pet--okay .eyes { animation: glance calc(12s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }

@keyframes glance { 0%, 40%, 62%, 100% { translate: 0 0; } 44%, 58% { translate: 2.6px 0; } }

/* Awake: the greeting, where no score is implied. Breathes like resting,
   glances like okay. */

.pet--live.pet--awake .core { animation: breathe-deep calc(6.8s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--awake .eyes { animation: glance calc(12s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }

/* Rested: 70 and over. */

.pet--live.pet--rested .body { animation:
  sway calc(5s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite,
  bob  calc(3.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--rested .core { animation: breathe-bright calc(3.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--rested .face { animation-duration: calc(3.4s * var(--pet-tempo) * var(--pet-speed, 1)); }
.pet--live.pet--rested .mouth { animation: grin calc(3.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }

.pet--live.pet--rested .flame-outer { animation:
  flicker calc(1.3s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite,
  lick    calc(1.9s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.pet--live.pet--rested .flame-core { animation:
  flicker calc(0.9s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite,
  lick    calc(1.5s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; animation-delay: -0.3s, -0.5s; }
/* the second eye burns out of step with the first */
.pet--live.pet--rested .eyes > g + g .flame-outer { animation-delay: -0.7s, -1.1s; }
.pet--live.pet--rested .eyes > g + g .flame-core { animation-delay: -1s, -1.6s; }

@keyframes bob            { 0%, 100% { translate: 0 0; } 50% { translate: 0 -1.6px; } }
@keyframes breathe-bright { 0%, 100% { scale: 1 1; } 50% { scale: 1.03 1.04; } }
@keyframes grin           { 0%, 100% { scale: 1 1; } 50% { scale: 1.05 1.14; } }
@keyframes flicker        { 0% { scale: 1 1; } 30% { scale: 1.06 1.1; } 55% { scale: 0.96 0.94; } 80% { scale: 1.03 1.05; } 100% { scale: 1 1; } }
@keyframes lick           { 0%, 100% { rotate: -2.5deg; } 50% { rotate: 2.5deg; } }

/* Reduced motion: the still drawing. */

@media (prefers-reduced-motion: reduce) {
  .pet--live * { animation: none !important; }
}

/* ── The mood layer: the page follows the pet's night ───────────────────────

   Everything is keyed on one class on the root, the same word as the pet's
   look — mood--resting, mood--tired, mood--okay, mood--rested — so the two can
   never disagree. --mood is a colour already in the palette: the low night is
   blush, the good one sun, the middle sage, and nothing-yet is sky.

   Four opt-in classes on the same root:
     tint-canvas   the canvas and cards take a wash of --mood (--tint percent)
     tint-accents  the active tab and the hairlines lean toward it
     fx-halo-on    two flat ellipses of --mood behind the pet
     fx-motes-on   eight small dots of --mood drifting round it
   The two tints are on. Halo and motes are off: the pet stands inside its
   orbit, and a wash behind it fights the ellipse; see moodClasses in pet.go.
   --tint 6 and --fx 1 keep the wash faint, and --pet-tempo 0.225 sets the
   pace. */

:root { --tint: 6; --fx: 1; --pet-tempo: 0.225; }

.mood--rested  { --mood: var(--sun); }
.mood--okay    { --mood: var(--sage); }
.mood--tired   { --mood: var(--blush); }
.mood--resting,
.mood--awake   { --mood: var(--sky); }

.tint-canvas {
  --canvas: color-mix(in oklab, var(--canvas-0), var(--mood) calc(var(--tint) * 1%));
  --white:  color-mix(in oklab, var(--white-0),  var(--mood) calc(var(--tint) * 0.4%));
}
/* The score does not lean toward the mood: it is set in ink, because a warm
   grey number read as a faded one. */
.tint-accents { --line: color-mix(in oklab, var(--line-0), var(--mood) calc(var(--tint) * 2%)); }
.tint-accents .tabs > a[aria-current="page"] { color: color-mix(in oklab, var(--ink), var(--mood) 30%); }

/* a change of mood fades in rather than snaps */
body, .card, .list, .tabs, .notice { transition: background-color 1.4s ease, border-color 1.4s ease; }

/* the effects layer, behind the stage */
.hero { position: relative; }
/* Width stated, not implied by left and right: an absolutely placed <svg>
   takes its own intrinsic width otherwise, and pushed the page sideways. */
.fx { position: absolute; left: 0; top: 0; width: 100%; height: 260px; z-index: 0; pointer-events: none;
  overflow: visible; display: block; }
.hero > :not(.fx) { position: relative; z-index: 1; }
.fx * { transform-box: fill-box; transform-origin: 50% 50%; }
.fx-halo, .fx-motes { display: none; fill: var(--mood); }
.fx-halo-on  .fx-halo  { display: block; }
.fx-motes-on .fx-motes { display: block; }
.fx-halo ellipse { opacity: .55; }
.fx-halo ellipse + ellipse { opacity: .45; }
.fx-halo { opacity: calc(.13 * var(--fx)); animation: halo calc(4.4s * var(--pet-tempo) * var(--pet-speed, 1)) ease-in-out infinite; }
.mood--resting .fx-halo,
.mood--awake   .fx-halo { opacity: calc(.10 * var(--fx)); animation-duration: calc(6.8s * var(--pet-tempo) * var(--pet-speed, 1)); }
.mood--tired   .fx-halo { opacity: calc(.11 * var(--fx)); animation-name: halo-sigh; animation-duration: calc(5.6s * var(--pet-tempo) * var(--pet-speed, 1)); }
.mood--rested  .fx-halo { opacity: calc(.16 * var(--fx)); animation-name: halo-bright; animation-duration: calc(3.4s * var(--pet-tempo) * var(--pet-speed, 1)); }
@keyframes halo        { 0%, 100% { scale: 1 1; } 50% { scale: 1.035 1.045; } }
@keyframes halo-sigh   { 0%, 100% { scale: 1 1; } 34%, 52% { scale: 1.03 1.04; } 68% { scale: 1 1; } }
@keyframes halo-bright { 0%, 100% { scale: 1 1; } 50% { scale: 1.05 1.06; } }
/* eight dots; each takes its own delay so they never move as one: embers
   rising when rested, drifting when okay, sinking when tired, a twinkle when
   resting */
.fx-motes circle { opacity: 0; animation: drift calc(9s * var(--pet-tempo) * var(--pet-speed, 1)) linear infinite; }
.fx-motes circle:nth-child(2) { animation-delay: -1.1s; } .fx-motes circle:nth-child(3) { animation-delay: -2.3s; }
.fx-motes circle:nth-child(4) { animation-delay: -3.4s; } .fx-motes circle:nth-child(5) { animation-delay: -4.6s; }
.fx-motes circle:nth-child(6) { animation-delay: -5.7s; } .fx-motes circle:nth-child(7) { animation-delay: -6.9s; }
.fx-motes circle:nth-child(8) { animation-delay: -8.0s; }
.mood--rested  .fx-motes circle { animation-name: ember; animation-duration: calc(6s * var(--pet-tempo) * var(--pet-speed, 1)); }
.mood--tired   .fx-motes circle { animation-name: sink;  animation-duration: calc(12s * var(--pet-tempo) * var(--pet-speed, 1)); }
.mood--resting .fx-motes circle { animation-name: twinkle; animation-duration: calc(14s * var(--pet-tempo) * var(--pet-speed, 1)); }
@keyframes drift   { 0% { translate: 0 14px; opacity: 0; } 20%, 70% { opacity: calc(.45 * var(--fx)); } 100% { translate: 4px -40px; opacity: 0; } }
@keyframes ember   { 0% { translate: 0 0; opacity: 0; scale: 1; } 15%, 55% { opacity: calc(.6 * var(--fx)); } 100% { translate: 6px -70px; opacity: 0; scale: .4; } }
@keyframes sink    { 0% { translate: 0 -10px; opacity: 0; } 25%, 65% { opacity: calc(.35 * var(--fx)); } 100% { translate: -3px 30px; opacity: 0; } }
@keyframes twinkle { 0%, 100% { opacity: 0; } 50% { opacity: calc(.4 * var(--fx)); } }

@media (prefers-reduced-motion: reduce) {
  .fx * { animation: none !important; }
  body, .card, .list, .tabs, .notice { transition: none; }
}

/* The privacy policy and the terms of use: long text, set to read. */
.legal-back { text-align: left; }
.legal { display: flex; flex-direction: column; gap: 12px; color: var(--ink-2); font-size: 15px; line-height: 1.55; }
.legal h1 { color: var(--ink); }
.legal h2 { color: var(--ink); font-size: 19px; margin-top: 10px; }
.legal ul { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; }
.legal a { color: var(--ink); text-underline-offset: 3px; }
.foot nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 0; }
.foot .sep { padding: 0 7px; }
.privacy-link a { color: var(--ink-2); text-underline-offset: 3px; }
