/* ==========================================================================
   THE ADD DIALOG, IN CODE — /add/
   Three sections share one recreation of `CaptureDialog`, sitting over the
   Home tab the way it does on the wall. Each section supplies only a camera
   track; the dialog itself is authored once, by tools/add-build-tab.py.

   WHAT THIS FILE IS, AND IS NOT
   -----------------------------
   The camera, the bezel and the screen are the shared engine in wall-demo.css.
   The wall BEHIND the dialog is walltab-demo.css — the Home tab /wall-tablet/
   already recreates, reused whole rather than drawn a second time, because the
   Add button lives in that tab's rail and a dialog floating on nothing would
   be making a different claim than the product does. Both load before this.

   So what is here is only the dialog: its shell, its six panels, and the three
   camera tracks. Every rule is scoped under `.cd`, `.cd-scrim` or a
   `data-track` of this page's, so nothing can reach either file it sits on.

   AUTHORED AT 1284 x 883 like every kit on this site — and for once with no
   conversion to do, because the wall renders at 1284 CSS px, so the dialog's
   `sm:max-w-lg` really is 512 of these pixels.

   THE ONE STRUCTURAL IDEA HERE
   ----------------------------
   The card is centred, as Radix centres it, and its height changes with the
   step — CaptureDialog animates it (`motion.div animate={{ height: bodyH }}`,
   0.32s, the app's own curve). Centred growth moves both edges, so a camera
   pinned to the card's TOP would drift; its CENTRE, (642, 441.5), never moves.
   Every shot below is written as an offset from that fixed point, and the
   per-step body heights are declared here so the arithmetic in the track
   comments can be checked against something.

       step        --bh   card   top .. bottom
       compose      208    362   260 .. 622
       compose+strip 264   418   232 .. 650
       waiting      228    382   250 .. 632
       found        398    552   165 .. 717
       done         248    402   240 .. 642
       inbox        384    538   172 .. 710   (264/418, 144/298, 120/274 as it empties)

   Values follow CaptureDialog.jsx and its panels; the generator's docstring
   carries the file-and-line citations for each one.
   ========================================================================== */

.cd {
  /* The wall's Honey theme, which is what walltab-demo.css draws the tab in.
     Named here rather than repeated, because the dialog is accent-coloured in
     eleven places and a second hue would read as a different product. */
  --acc: #d97706;
  --acc-t: #b45309;
  --bd: rgba(120, 92, 54, 0.18);
  --card: #fffdf9;
  --sidebar: #f7f3ec;
  --dim: rgba(71, 62, 48, 0.62);
}

/* ── the scrim ───────────────────────────────────────────────────────────── */
/* Over the rail and the strip (both z-index 6) and over the tab's own compose
   dialog (8), because on the wall this one is opened from the rail and covers
   everything. */
.cd-scrim {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: rgba(15, 18, 20, 0.45);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.28s ease, visibility 0.28s;
}
.cd-scrim.is-on { opacity: 1; visibility: visible; }

.cd {
  width: 512px;
  border-radius: 28px;
  background: var(--card);
  border: 1px solid var(--bd);
  box-shadow: 0 34px 80px -20px rgba(16, 18, 38, 0.5);
  overflow: hidden;
  color: #1f2937;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-align: left;
  transform: translateY(12px) scale(0.97);
  transition: transform 0.3s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.cd-scrim.is-on .cd { transform: none; }

.cd .cd-i { width: 1em; height: 1em; display: block; flex: none; }
.cd-i12 { font-size: 12px; } .cd-i14 { font-size: 14px; }
.cd-i16 { font-size: 16px; } .cd-i20 { font-size: 20px; }
.cd-i24 { font-size: 24px; }
.cd-brand { font-size: 14px; width: 1em; height: 1em; display: block; flex: none; }
.cd-dim { color: var(--dim); }
.cd-ok { color: #22c55e; }

/* ── the header ──────────────────────────────────────────────────────────── */
/* 85px: 20 top, the 48px disc, 16 bottom, 1 border. The gradient is the
   component's own — accent-soft at 48% over white, falling to the card. */
.cd-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--bd);
  background: linear-gradient(180deg, rgba(251, 214, 158, 0.42), var(--card));
}
.cd-mark {
  width: 48px; height: 48px; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: rgba(255, 255, 255, 0.72);
  color: var(--acc-t);
}
/* Heavier than lucide's default 2: the nav glyph is read against a flat bar,
   this one against a 48px disc, which makes the same weight look thinner. */
.cd-plus { stroke-width: 2.75; }

/* The six title/subtitle pairs are all in the DOM, stacked in one grid cell,
   and the step class raises one. Cross-fading them is what makes the step
   change read as one screen changing its mind rather than as a cut. */
.cd-htext { flex: 1; min-width: 0; display: grid; }
.cd-h {
  grid-area: 1 / 1;
  opacity: 0;
  transition: opacity 0.22s ease;
}
.cd-h b { display: block; font-size: 16px; font-weight: 600; line-height: 1.15; }
.cd-h i { display: block; margin-top: 2px; font-size: 12px; font-style: normal; color: var(--dim); }
.cd[data-cd-step="compose"]  .cd-h[data-cd-h="compose"],
.cd[data-cd-step="waiting"]  .cd-h[data-cd-h="waiting"],
.cd[data-cd-step="found"]    .cd-h[data-cd-h="found"],
.cd[data-cd-step="done"]     .cd-h[data-cd-h="done"],
.cd[data-cd-step="inbox"]    .cd-h[data-cd-h="inbox"] { opacity: 1; }

.cd-close { width: 32px; height: 32px; display: grid; place-items: center; flex: none; }

/* ── the body ────────────────────────────────────────────────────────────── */
.cd-body {
  position: relative;
  height: var(--bh, 208px);
  overflow: hidden;
  transition: height 0.32s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.cd-panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 24px 20px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.22s ease, visibility 0.22s;
}
.cd-panel--mid { justify-content: center; align-items: center; text-align: center; gap: 18px; }
.cd[data-cd-step="compose"]  .cd-panel[data-cd-panel="compose"],
.cd[data-cd-step="waiting"]  .cd-panel[data-cd-panel="waiting"],
.cd[data-cd-step="found"]    .cd-panel[data-cd-panel="found"],
.cd[data-cd-step="done"]     .cd-panel[data-cd-panel="done"],
.cd[data-cd-step="inbox"]    .cd-panel[data-cd-panel="inbox"] { opacity: 1; visibility: visible; }

.cd[data-cd-step="compose"]  { --bh: 208px; }
.cd[data-cd-step="waiting"]  { --bh: 228px; }
.cd[data-cd-step="found"]    { --bh: 398px; }
.cd[data-cd-step="done"]     { --bh: 248px; }
.cd[data-cd-step="inbox"]    { --bh: 384px; }
/* Chapter 3's copy carries the queue, so its composer is 56px taller. The
   strip is in every copy of the markup and shown only here — the alternative
   is a second generator output that differs by one element. */
.cd.has-strip[data-cd-step="compose"] { --bh: 264px; }
/* The queue shrinks as it empties, because CaptureDialog animates the body to
   whatever is left in it. A row is 108 tall and the gap is 12, so three rows
   and the panel's own 36 of padding make 384; two make 264 and one makes 144.
   The camera's second inbox shot is framed to hold the card at both of the
   last two, which is why the accept happens in the first shot and the dismiss
   in the second. */
.cd[data-cd-step="inbox"][data-cd-rows="2"] { --bh: 264px; }
.cd[data-cd-step="inbox"][data-cd-rows="1"] { --bh: 144px; }
.cd[data-cd-step="inbox"][data-cd-rows="0"] { --bh: 120px; }

/* ── the footer ──────────────────────────────────────────────────────────── */
/* Back on the left, the forward action on the right. WAITING has neither, and
   gets no bar rather than an empty one. */
.cd-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 24px;
  border-top: 1px solid var(--bd);
}
.cd[data-cd-step="waiting"] .cd-foot { visibility: hidden; }

.cd-back, .cd-go {
  display: none;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 16px;
  border-radius: 14px;
  font-size: 14px;
  font-weight: 600;
}
.cd-back { margin-right: auto; color: var(--dim); }
.cd[data-cd-step="found"] .cd-back,
.cd[data-cd-step="inbox"] .cd-back { display: inline-flex; }

.cd-go { margin-left: auto; background: var(--acc); color: #fff; font-weight: 700; }
.cd[data-cd-step="compose"] .cd-go[data-cd-go="compose"],
.cd[data-cd-step="found"]   .cd-go[data-cd-go="found"],
.cd[data-cd-step="done"]    .cd-go[data-cd-go="done"] { display: inline-flex; }

/* ── the composer ────────────────────────────────────────────────────────── */
.cd-strip {
  display: none;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 14px;
  border-radius: 16px;
  border: 1px solid rgba(217, 119, 6, 0.45);
  background: rgba(217, 119, 6, 0.08);
  font-size: 14px;
  font-weight: 600;
}
.cd.has-strip .cd-strip { display: flex; }
.cd-strip b {
  min-width: 22px; height: 22px; padding: 0 6px; border-radius: 999px;
  display: grid; place-items: center; flex: none;
  background: var(--acc); color: #fff; font-size: 11px; font-weight: 700;
}
.cd-strip > span { flex: 1; min-width: 0; }

/* The textarea. `rows={5}` plus the app's 14px/1.45 and its 2xl radius; the
   placeholder is a real element so it can be faded out as the typing starts,
   which is what a real one does. */
.cd-ta {
  flex: 1;
  position: relative;
  padding: 11px 13px;
  border-radius: 16px;
  border: 1px solid var(--bd);
  background: #fff;
  font-size: 14px;
  line-height: 1.45;
  color: #1f2937;
}
.cd-ph { color: rgba(71, 62, 48, 0.45); transition: opacity 0.2s ease; }
.cd.is-typing .cd-ph, .cd.is-typed .cd-ph { opacity: 0; position: absolute; }
.cd-caret {
  display: none;
  width: 1.5px; height: 16px; vertical-align: -3px;
  background: var(--acc);
  animation: cd-caret 1s steps(1) infinite;
}
.cd.is-typing .cd-caret, .cd.is-typed .cd-caret { display: inline-block; }
@keyframes cd-caret { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }

.cd-tools { display: flex; align-items: center; gap: 8px; height: 36px; }
.cd-tool {
  width: 36px; height: 36px; border-radius: 12px;
  display: grid; place-items: center; flex: none;
  border: 1px solid var(--bd); background: #fff; color: var(--acc-t);
}
/* The fourth way in — the family's own phones. It carries no explanatory
   subtext; three marks people already recognise say what "Others" means
   faster than a sentence could (Composer.jsx's ChannelsButton). */
.cd-others {
  display: flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 10px; border-radius: 12px;
  border: 1px solid var(--bd); background: #fff;
}
.cd-others b { font-size: 12px; font-weight: 700; }

/* ── waiting ─────────────────────────────────────────────────────────────── */
.cd-spin {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(217, 119, 6, 0.1);
  color: var(--acc-t);
}
.cd-spin .cd-i { animation: cd-spin 1s linear infinite; }
@keyframes cd-spin { to { transform: rotate(360deg) } }
.cd-wait-h { margin: 0; font-size: 14px; font-weight: 600; }
.cd-wait-b { margin: 4px 0 0; font-size: 12px; color: var(--dim); max-width: 300px; }

/* ── the trail of what auto-route already did ────────────────────────────── */
.cd-trail {
  padding: 14px;
  border-radius: 16px;
  border: 1px solid rgba(217, 119, 6, 0.4);
  background: rgba(217, 119, 6, 0.07);
}
.cd-trail-head {
  margin: 0; display: flex; align-items: center; gap: 8px;
  color: var(--acc-t);
}
.cd-trail-head span {
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--dim);
}
.cd-trail-row { display: flex; align-items: flex-start; gap: 12px; margin-top: 10px; }
.cd-trail-ic {
  width: 32px; height: 32px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.72); color: var(--acc-t);
}
.cd-trail-txt { flex: 1; min-width: 0; }
.cd-trail-txt b { display: block; font-size: 14px; font-weight: 600; }
.cd-trail-txt small { display: block; font-size: 12px; color: var(--dim); }
.cd-undo {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 4px 8px; border-radius: 10px;
  font-size: 12px; font-weight: 600; color: var(--dim);
}
.cd-trail-note { margin: 10px 0 0; font-size: 12px; color: var(--dim); }

/* ── the review card ─────────────────────────────────────────────────────── */
.cd-card { padding: 16px; border-radius: 16px; border: 1px solid var(--bd); background: #fff; }
.cd-card-t {
  margin: 0; display: flex; align-items: flex-start; gap: 8px;
  font-size: 14px; font-weight: 600; line-height: 1.3;
}
.cd-x2 { margin-left: auto; color: var(--dim); flex: none; }
.cd-card-s { margin: 4px 0 0; font-size: 12px; color: var(--dim); }

.cd-acts { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
/* One box per action, sized for a thumb, styled only by whether it is
   selected. The accent marks what is SELECTED, never what is recommended —
   position and the initial tick carry that, and both are information rather
   than decoration (FoundPanel.jsx's ActionRow). */
.cd-act {
  display: flex; align-items: flex-start; gap: 12px;
  min-height: 44px; padding: 11px 12px;
  border-radius: 12px; border: 1px solid var(--bd); background: #fff;
  transition: border-color 0.22s ease, background 0.22s ease;
}
.cd-act b { font-size: 13px; font-weight: 600; line-height: 1.3; }
/* 20px and square, with a literal 6px radius. The default read as a radio —
   "pick exactly one" — when every one of these rows can be ticked. */
.cd-box {
  width: 20px; height: 20px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  border: 2px solid var(--bd); background: transparent; color: #fff;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.cd-box .cd-i { opacity: 0; transition: opacity 0.16s ease; }
.cd-act.is-on { border-color: var(--acc); background: rgba(217, 119, 6, 0.1); }
.cd-act.is-on .cd-box { border-color: var(--acc); background: var(--acc); }
.cd-act.is-on .cd-box .cd-i { opacity: 1; }

/* ── what happened, in the active voice ──────────────────────────────────── */
.cd-res { margin: 0; display: flex; align-items: flex-start; gap: 12px; font-size: 13px; line-height: 1.35; }
.cd-res .cd-i { margin-top: 1px; }

/* ── the queue ───────────────────────────────────────────────────────────── */
.cd-row {
  padding: 12px;
  border-radius: 16px;
  border: 1px solid var(--bd);
  background: #fff;
  transition: opacity 0.3s ease, transform 0.3s ease, margin 0.3s ease, height 0.3s ease;
}
.cd-row.is-over { border-color: rgba(239, 68, 68, 0.55); background: rgba(239, 68, 68, 0.06); }
/* Accepted or dismissed, a row leaves. It collapses rather than vanishing, or
   the two rows below it jump by 120px in one frame. */
.cd-row.is-gone {
  opacity: 0; transform: translateX(-14px);
  height: 0; padding-top: 0; padding-bottom: 0; margin-top: -12px; border-width: 0;
  overflow: hidden;
}
.cd-row-top { display: flex; align-items: flex-start; gap: 10px; }
.cd-dot { width: 8px; height: 8px; border-radius: 50%; background: #ef4444; flex: none; margin-top: 6px; }
.cd-row-txt { flex: 1; min-width: 0; }
.cd-row-txt > b { display: block; font-size: 14px; font-weight: 600; line-height: 1.3; }
.cd-row-meta { display: flex; align-items: center; gap: 6px; margin-top: 4px; min-width: 0; }
.cd-src {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 2px 7px; border-radius: 999px;
  border: 1px solid var(--bd); background: var(--sidebar);
  font-size: 10px; font-style: normal; font-weight: 600; color: var(--dim);
}
.cd-row-meta small {
  flex: 1; min-width: 0; font-size: 12px; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cd-row-btns { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.cd-accept {
  flex: 1; min-width: 0; height: 36px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--acc); color: #fff; font-size: 12px; font-weight: 700;
}
.cd-open {
  width: 36px; height: 36px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--bd); color: var(--dim);
}

/* "All caught up" — the branch of the same panel that shows once every row has
   been dealt with. In the DOM at all times; `data-cd-rows="0"` raises it, which
   is the same attribute that sets the body height, so the panel and the frame
   around it can never disagree about how much is left. */
.cd-empty { display: none; margin: auto 0; text-align: center; }
.cd[data-cd-rows="0"] .cd-empty { display: block; }
.cd-empty-h { margin: 0; font-size: 14px; font-weight: 600; }
.cd-empty-b { margin: 4px 0 0; font-size: 12px; color: var(--dim); }

/* ── the phones — chapter 2's satellites ─────────────────────────────────── */
/* Two handsets that slide out from behind the tablet, sit over the wall while
   something is sent from them, and go back. The idiom is the homepage's
   `.hero-tableau`, where the wall is surrounded by the surfaces that feed it;
   what is different here is that the wall is a real 1284px recreation in a
   bezel, and a bezel clips. So the satellites cannot orbit OUTSIDE it.

   They live in `.ad-phones` instead: a layer the size of the frame, over it,
   with `overflow: hidden` and the bezel's own radius. A parked phone is
   translated past that edge and is therefore invisible, and comes into view by
   sliding in — which is what "out from behind the tablet" looks like from the
   front. The layer is separate from `.ct-frame` rather than being it, because
   clipping the frame would clip its drop shadow and flatten the whole picture.

   AUTHORED IN PAGE PIXELS, not tab pixels. These sit on the bezel, not inside
   the wall, so `--fit` never scales them and their 9px type stays 9px. That is
   the whole reason a phone can be read here while the wall behind it cannot. */
.ad-tableau { position: relative; }

.ad-phones {
  position: absolute;
  /* The SCREEN's box, not the frame's: inset by the bezel's own 12px of padding
     and clipped to the screen's 15px radius (.ct-frame / .ct-stage in
     wall-demo.css). Two things follow. A parked phone is hidden at the edge of
     the glass rather than at the edge of the case, which is where something
     behind a tablet would appear from; and `right: 0` below means the screen's
     right edge, so a phone can be anchored to it at any width. */
  inset: 12px;
  z-index: 2;
  overflow: hidden;
  border-radius: 15px;
  pointer-events: none;
}

.ad-phone {
  --x: 0px;
  --r: 0deg;
  --s: 1;
  position: absolute;
  top: 50%;
  width: clamp(104px, 26%, 150px);
  aspect-ratio: 9 / 19;
  padding: 5px;
  border-radius: 18px;
  background: #14181b;
  box-shadow: 0 26px 54px rgba(16, 18, 38, 0.38), 0 6px 16px rgba(16, 18, 38, 0.2);
  transform: translate(var(--x), -50%) rotate(var(--r)) scale(var(--s));
  transition: transform 720ms cubic-bezier(0.3, 0.9, 0.3, 1);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ANCHORED TO AN EDGE, NOT OFFSET FROM THE MIDDLE. It was `left: 50%` and a
   translate measured as a percentage of the phone's own width, which cannot be
   right at more than one breakpoint: the phone is 173px in a 523px screen on a
   desktop and 122px in a 338px one on a handset, so the offset that puts it
   near the edge on the first (101% of itself) puts it through the bezel on the
   second, where the ceiling is 88%. Anchoring removes the arithmetic — the rest
   position is a fixed 10px gap from the glass at every width.

   Parked is 130% of its own width beyond that anchor, so it is wholly outside
   the clip with 30% to spare, and the tilt and the slight shrink make arriving
   read as a settle rather than a slide. */
.ad-phone.from-right { right: 0; --x: 130%; --r: 7deg; --s: 0.94; }
.ad-phone.from-left  { left: 0;  --x: -130%; --r: -7deg; --s: 0.94; }

.ad-phone.is-out { --r: 0deg; --s: 1; }

/* A phone stops on the SIDE IT CAME IN FROM and never crosses to the middle.
   Centred, it stands on the very thing it is about to change — the Today row
   about to land, the photograph about to appear in the slideshow — and the
   reader watches the cause obscure the effect. Held against the glass, both are
   on screen at once, which is the whole argument of the chapter. */
.ad-phone.from-right.is-out { --x: -10px; }
.ad-phone.from-left.is-out  { --x: 10px; }

.ad-phone .cd-i { width: 1em; height: 1em; display: block; flex: none; }

.ad-phone-screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: 14px;
  background: #fffdf9;
  color: #1f2937;
  display: flex;
  flex-direction: column;
}

.ad-app-bar {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  padding: 7px 8px;
  border-bottom: 1px solid rgba(120, 92, 54, 0.14);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.01em;
}
.ad-app-bar img, .ad-app-bar .cd-i { width: 11px; height: 11px; flex: none; }
.ad-app-bar i { font-style: normal; font-weight: 600; opacity: 0.5; }

/* --- the mail --- */
.ad-mail { flex: 1; padding: 8px; }
.ad-mail-from { margin: 0; font-size: 8.5px; font-weight: 800; color: var(--acc-t, #b45309); }
.ad-mail-sub { margin: 3px 0 0; font-size: 10px; font-weight: 700; line-height: 1.25; }
.ad-mail-body { margin: 5px 0 0; font-size: 8.5px; line-height: 1.4; color: rgba(71, 62, 48, 0.66); }

.ad-share {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin: 8px;
  height: 24px;
  border-radius: 8px;
  background: #d97706;
  color: #fff;
  font-size: 8.5px;
  font-weight: 800;
}

/* The confirmation takes the screen, because on a phone it does. */
.ad-done {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px;
  background: #fffdf9;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 260ms ease, visibility 260ms;
}
.ad-phone.is-done .ad-done { opacity: 1; visibility: visible; }
.ad-done-mark {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(34, 197, 94, 0.14); color: #15803d;
}
.ad-done-mark .cd-i { width: 14px; height: 14px; }
.ad-done-h { margin: 0; font-size: 10px; font-weight: 800; }
.ad-done-b { margin: 0; font-size: 8.5px; color: rgba(71, 62, 48, 0.66); }

/* --- the bot chat --- */
.ad-tg-log {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  background: #f4f1ea;
}
.ad-msg {
  max-width: 88%;
  padding: 5px 7px;
  border-radius: 10px;
  font-size: 8.5px;
  line-height: 1.35;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 240ms ease, transform 300ms cubic-bezier(0.2, 0.9, 0.3, 1);
}
.ad-msg.is-on { opacity: 1; transform: none; }
.ad-msg--me { align-self: flex-end; background: #d6f3c9; border-bottom-right-radius: 3px; }
.ad-msg--bot {
  align-self: flex-start;
  display: flex; align-items: flex-start; gap: 4px;
  background: #fff; border-bottom-left-radius: 3px;
  color: #15803d; font-weight: 700;
}
.ad-msg--bot .cd-i { width: 10px; height: 10px; flex: none; margin-top: 1px; }
.ad-msg--photo { padding: 3px; }
.ad-msg--photo img { display: block; width: 100%; border-radius: 7px; }

/* ── the Chores tab, borrowed whole ──────────────────────────────────────── */
/* Chapter 2's third send is a text that ticks three of Ava's chores off, and
   the only honest place to show that is the Chores tab — so the wall goes
   there. The markup is /chores/'s own (assets/chores-tab.html, lifted by
   tools/chores-extract-tab.py) and chores-demo.css draws it; nothing about
   either is re-implemented here.

   A NOTE ON WHAT THIS IS. The wall does not change tabs by itself — a capture
   that ticks a chore updates the widget wherever it already is. The switch is
   the tour showing you where the change landed, the same class of move as
   /wall-tablet/'s hero scrolling the column with nobody touching it. What IS
   the product is everything inside the tab: the rows striking, the ring
   closing, the count and the coin chip moving. */
.wt-chores {
  z-index: 2;              /* over the Home column, under the strip and rail (6) */
  background: #fdf9f3;
  opacity: 0;
  visibility: hidden;
  transition: opacity 420ms ease, visibility 420ms;
}
.wt-chores.is-on { opacity: 1; visibility: visible; }

/* The rail's own selected state follows, because on the wall it would. */
.ct-stage[data-track="addchan"] .wt-tab { transition: background 300ms ease, color 300ms ease; }

/* The wall's own new row, marked as new for the moment it lands. `.wt-ev` is
   walltab-demo.css's; this only adds the arrival, and only under this page's
   tracks so /wall-tablet/'s Today list is untouched. */
.ct-stage[data-track="addchan"] .wt-ev.is-new {
  animation: ct-add-row 520ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
}
@keyframes ct-add-row {
  from { opacity: 0; transform: translateY(-6px); background: rgba(217, 119, 6, 0.16); }
  to   { opacity: 1; transform: none; }
}

/* ── the badge on the rail's Add button ──────────────────────────────────── */
/* CaptureMenu's pill, drawn here rather than in the generated tab markup: the
   tab is /wall-tablet/'s too, and that page's wall has nothing waiting. The
   count belongs to the page making the claim, so it is set per track. */
.ct-stage[data-track^="add"] .wt-addbtn { position: relative; }
.ct-stage[data-track^="add"] .wt-addbtn::after {
  content: attr(data-cd-badge);
  position: absolute; top: -2px; right: 8px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; display: grid; place-items: center;
  background: #d97706; color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 2px rgba(255, 253, 248, 0.95);
}
.ct-stage[data-track^="add"] .wt-addbtn:not([data-cd-badge])::after { display: none; }

/* ==========================================================================
   THE CAMERA
   Every shot is at z=2 — the scale at which the dialog's own 12-14px type is
   legible once the stage is scaled down to a page column. At z=2 the view is
   642 x 441.5 tab pixels, so tx=-321 centres the 512px card horizontally and
   ty is simply the top of what the shot should show.

   The constraint from PLAN-wall-tablet-page.md holds: |tx| <= 1284(1 - 1/z)
   and |ty| <= 883(1 - 1/z), which at z=2 is 642 and 441.5. Every value below
   is inside both, so no shot runs off into the page behind the tab.
   ========================================================================== */

/* THE ADD BUTTON'S SHOT, shared by tracks 1 and 3 ---------------------------
   Both chapters open the way the family does: at the `+` on the wall's own
   rail, not at a dialog that is already there. `.wt-addbtn` measures
   x 1243..1266, y 831..872 in tab space — the far bottom-right corner, 18px in
   from the edge, which is where MobileBottomNav puts it.

   z=2.3 is as close as that corner can be framed. At that zoom the view is
   558 x 384 and the pan limits are |tx| <= 725 and |ty| <= 499, both of which
   put the view's far edge exactly on the tab's — so tx=-725, ty=-499 IS the
   bottom-right corner, and the button lands 41px in from the right of it and
   52 up from the bottom. Zooming further does not bring the button any nearer
   the middle of the frame; it only crops the rail around it. */

/* --- 1. the hero: press Add, share it, watch it read, say yes --------------
   25s. It opens on the Add button being pressed and closes on the ✕ being
   pressed, so the chapter is one person's session rather than a dialog that
   appears and evaporates. Between those, the typing alone is 2.8s and the
   "check it over" beat is the argument of the page, so nothing here compresses.

   Seven shots: the wall, the Add button, back out to the wall while the dialog
   opens over it, the composer, the top of the review card, its actions and
   Save, then what it did — which is also where the ✕ is (card 240..642, the
   ✕ at about 843, 272). Card tops: compose 260, found 165, done 240. */
@keyframes ct-add-flow {
  0%,  2%   { --z: 1;   --tx: 0;    --ty: 0; }      /* the wall as it stands */
  10%, 18%  { --z: 2.3; --tx: -725; --ty: -499; }   /* in on the Add button */
  25%, 29%  { --z: 1;   --tx: 0;    --ty: 0; }      /* out; the dialog opens here */
  36%, 50%  { --z: 2;   --tx: -321; --ty: -250; }   /* the composer */
  57%, 68%  { --z: 2;   --tx: -321; --ty: -160; }   /* what it found */
  73%, 80%  { --z: 2;   --tx: -321; --ty: -280; }   /* the actions, and Save */
  85%, 94%  { --z: 2;   --tx: -321; --ty: -235; }   /* what it did, and the ✕ */
  98%, 100% { --z: 1;   --tx: 0;    --ty: 0; }      /* out */
}
.ct-stage[data-track="addflow"] .ct-cam { animation: ct-add-flow 25s infinite; }

/* --- 2. the channels: three sends from two phones -------------------------
   30s. The camera has three jobs and every one of them happens behind a phone
   or after one has left, so the board never appears to move on its own.

   THE ZOOM on the Today list, for the first send. At z=1 that list renders its
   13px rows at 5.5px on screen — you can see a row appear and you cannot read
   it, which is half a payoff. z=2.1 puts them at 11.5px. The list is 1180 wide
   against a 611-wide window, so the owner column at the far right is out of
   frame; the time and the event name are what the row is. The mail phone rests
   right of centre (`.ad-phone--mail.is-out`) so it is not standing on them.

   THE SCROLL is `--sc`, the wall's own — declared `inherits: true` in
   walltab-demo.css and animated here, so scroll and camera stay one animation
   on one clock. 717 is the value /wall-tablet/'s hero track uses to bring the
   slideshow into view. It goes down behind the second phone and comes back up
   behind the third, which is why the photograph is sent before the chores
   message rather than after it: in the other order the board had to scroll back
   with nothing covering it.

   THE THIRD SHOT is Ava's meter on the Chores tab. Her card is x 440..844,
   y 48..720 in tab space — 404 wide and 672 tall, which is the wrong shape for
   a 1.45 frame, so it cannot be filled. z=1.55 is the compromise that holds all
   of it (85..655 covers the ring at 80..230, the chips, and all five rows down
   to 651) while putting the 15px row titles at 9.8px. Her neighbours fill the
   rest of the window, which is what the tab looks like.

   Everything these phones send auto-executes, so there is no badge, no dialog
   and no queue in this chapter. Chapter 3 brings its own. */
@keyframes ct-add-chan {
  0%,   2%   { --z: 1;    --tx: 0;    --ty: 0;    --sc: 0; }     /* the wall as it stands */
  6%,  23%   { --z: 2.1;  --tx: -45;  --ty: -250; --sc: 0; }     /* in on Today, for the row that lands in it */
  26%        { --z: 1;    --tx: 0;    --ty: 0;    --sc: 0; }     /* out */
  31%, 48%   { --z: 1;    --tx: 0;    --ty: 0;    --sc: 717; }   /* down to the slideshow, for the photograph */
  54%, 64%   { --z: 1;    --tx: 0;    --ty: 0;    --sc: 0; }     /* back up, behind the third phone */
  70%, 90%   { --z: 1.55; --tx: -228; --ty: -85;  --sc: 0; }     /* in on Ava's meter, on the Chores tab */
  96%, 100%  { --z: 1;    --tx: 0;    --ty: 0;    --sc: 0; }     /* out, and back to Home */
}
.ct-stage[data-track="addchan"] .ct-cam { animation: ct-add-chan 30s infinite; }

/* --- 3. the queue ---------------------------------------------------------
   26s. Same opening as track 1 — the badge on the Add button is the reason
   anybody opens this screen, so the chapter starts by showing it — and then
   all THREE rows are dealt with in turn, each one ringed and each answered by
   the toast the wall actually raises.

   Only two shots hold the list, and the split is decided by the card's height
   rather than by the story: the card is 538 tall with three rows (172..710),
   418 with two (232..650), 298 with one (292..590) and 274 once the list is
   empty (304..578). No single frame holds the first and the last. So the
   three-row state gets 168..609, and 230..671 holds every state after it —
   which is why the first decision is made in the first shot and the other two
   in the second. Card tops: compose+strip 232, inbox 172. */
@keyframes ct-add-inbox {
  0%,  2%   { --z: 1;   --tx: 0;    --ty: 0; }      /* the wall as it stands */
  10%, 18%  { --z: 2.3; --tx: -725; --ty: -499; }   /* in on the Add button, badge and all */
  25%, 29%  { --z: 1;   --tx: 0;    --ty: 0; }      /* out; the dialog opens here */
  34%, 40%  { --z: 2;   --tx: -321; --ty: -228; }   /* "3 things waiting for you" */
  45%, 55%  { --z: 2;   --tx: -321; --ty: -168; }   /* the list, and the first decision */
  60%, 89%  { --z: 2;   --tx: -321; --ty: -230; }   /* the other two, and "All caught up" */
  94%, 100% { --z: 1;   --tx: 0;    --ty: 0; }      /* out */
}
.ct-stage[data-track="addinbox"] .ct-cam { animation: ct-add-inbox 26s infinite; }

/* ── with motion off ─────────────────────────────────────────────────────── */
/* The engine stills the camera; what is left is this page's own looping parts.
   The resting frame each track settles on is set in add-demo.js. */
@media (prefers-reduced-motion: reduce) {
  .cd-scrim, .cd, .cd-body, .cd-panel, .cd-h, .cd-act, .cd-box, .cd-row,
  .cd-ph { transition: none !important; }
  .cd-caret, .cd-spin .cd-i { animation: none !important; }
  .cd-caret { opacity: 1; }

  /* The engine parks every camera at z=1 — the whole tab, which is the right
     resting frame for a page whose subject is a board. Here the subject is a
     dialog 512px wide inside a 1284px wall, and at z=1 its 12px type is 5px on
     screen: the frame that carries the point would be unreadable. So each
     track holds its own shot instead. Specificity ties with wall-demo.css's
     rule and this file loads after it, which is the whole of why this works —
     the engine deliberately leaves those three properties without !important
     so a page can do exactly this. */
  .ct-stage[data-track="addflow"]  .ct-cam { --z: 2; --tx: -321; --ty: -160; }
  /* Chapter 2 rests on Ava's meter, which is the frame that carries its point:
     a wall that changed because somebody sent a text. */
  .ct-stage[data-track="addchan"]  .ct-cam { --z: 1.55; --tx: -228; --ty: -85; }
  .wt-chores, .ct-row, .ct-arc { transition: none !important; }
  .ct-stage[data-track="addinbox"] .ct-cam { --z: 2; --tx: -321; --ty: -168; }
}
