/* ============================================================
   The Direct Stay app, reproduced for the marketing site.

   A HAND PORT of the real product. Source of truth, all in
   ~/Code/web/directstay-app (read only from here, there is no build step
   joining the two repos and there should not be):

     components/app/Sidebar.tsx            + Sidebar.module.css
     components/app/AppShell.module.css    (the canvas / board / panel model)
     components/app/UserChip.module.css
     components/inbox/ConversationsPanel.tsx + .module.css
     components/inbox/InboxPanels.tsx        + .module.css
     components/icons/index.tsx            (nav glyphs, 24 box, 1.7 stroke)
     lib/inbox/preview-data.ts             (the conversations shown below)
     app/tokens.css                        (every value re-declared here)

   The point of the port is recognition: a visitor who signs up should land on
   the screen they were shown. So the structure is the app's structure, not a
   drawing of an app. In particular:

     - The sidebar is a WHITE FULLY ROUNDED CARD on a --mist board, not a
       column with a divider rule. Sidebar.module.css says in its own header
       that adding a hairline is what made it read as a table rather than a
       surface. The gap IS the division. Do not add a border-right here.
     - The board is the RECESS. It is mist, and the panels floating in it are
       the white cards.
     - The brand mark slot at the top of the sidebar is deliberately EMPTY.
       The flying badge in js/crm-spotlight.js parks in it and becomes the
       app's own logo. See .crm-ui__brand-mark.

   MEASURED DEVIATIONS from the app, all of them because a 1440px product is
   being shown inside a 980px illustration:
     - rail width 224px, not the app's --rail-expanded-w 248px
     - conversations panel 304px, not the app's 380px
     - board padding 10px is the app's; main padding is 10px, not the
       content well's 24px, which at this width would leave the conversation
       pane 340px and slice the bubbles
     - the row summary clamps to ONE line, not two, so four rows (one per
       channel, which is the whole claim) fit the 560px stage without the
       fourth being cut in half
   Type scale, radii, colours and every other value are the app's verbatim.

   Guest data is the app's own preview fixture. Never put real guest data here.
   ============================================================ */

.crm-ui {
  /* ---- app/tokens.css, verbatim ---- */
  --crm-violet-deep: #4c1d95;
  --crm-violet-bright: oklch(64% 0.2 294);
  --crm-accent: #7c3aed;
  --crm-accent-on: #fff;
  --crm-accent-text: #7c3aed;
  --crm-accent-subtle: rgb(124 58 237 / .10);
  --crm-accent-border: rgb(124 58 237 / .28);
  --crm-signal-yellow: #f5ff63;
  --crm-signal-blue: #01b2fd;
  --crm-success: #0b6b3a;
  --crm-cloud: #f3f3f5;
  --crm-mist: #f2f1f3;
  --crm-white: #fff;
  --crm-ink: #19171c;
  --crm-muted: #5d5962;
  --crm-line: rgb(76 29 149 / .14);

  --crm-radius: 16px;
  --crm-radius-sm: 10px;
  --crm-radius-xs: 6px;
  --crm-radius-pill: 999px;

  --crm-text-xs: 13px;
  --crm-text-sm: 15px;
  --crm-tracking-tight: -0.04em;
  --crm-leading-body: 1.5;
  --crm-tap: 44px;

  /* the app's board model */
  --crm-board-pad: 10px;
  --crm-rail-w: 224px;

  position: relative;
  width: min(980px, 100%);
  margin: 0 auto;
  border-radius: 28px;
  overflow: hidden;
  background: var(--crm-white);
  border: 1px solid var(--crm-line);
  /* Same lift as the site's own cards rather than a generic drop shadow. */
  box-shadow: 0 30px 80px -30px rgb(25 23 28 / .30), 0 2px 6px rgb(25 23 28 / .05);
  color: var(--crm-ink);
  font-family: var(--sans, "Switzer", "Avenir Next", Avenir, "Helvetica Neue", sans-serif);
  font-size: var(--crm-text-sm);
  line-height: var(--crm-leading-body);
  text-align: left;
}

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

/* ---------- browser chrome ---------- */
.crm-ui__chrome {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 38px;
  padding: 0 16px;
  background: var(--crm-cloud);
  border-bottom: 1px solid var(--crm-line);
}
.crm-ui__dots { display: flex; gap: 6px; }
.crm-ui__dots i { width: 9px; height: 9px; border-radius: 50%; background: rgb(76 29 149 / .18); }
.crm-ui__url { flex: 1; margin: 0; text-align: center; font-size: 11px; color: var(--crm-muted); }

/* ---------- the board (AppShell.module.css .board) ----------
   Mist ground, one radius, and the panels inside it are the white cards. No
   hairline anywhere inside: a gap between two rounded cards on a darker
   ground already reads as a division. */
.crm-ui__board {
  display: grid;
  /* minmax(0, rail) not a fixed track: below 1400px the window itself is
     narrower than 980, and a rigid 224px rail plus a rigid 304px conversations
     panel is what amputated the conversation pane. Measured before the fix:
     the pane fell to 210px at 1280 and 2px at 1024 while the copy beside it
     read "You answer from here". The rail now gives up width first, the
     conversations panel second, and the pane holds a 300px floor (see
     .crm-ui__main) at every width the section pins at. */
  grid-template-columns: minmax(0, var(--crm-rail-w)) minmax(0, 1fr);
  /* An auto row inside a fixed-height grid sizes to CONTENT and quietly
     overruns it: the sidebar was rendering 505.75px inside a 500px board and
     its bottom corner was being sliced off by .crm-ui's overflow:hidden.
     minmax(0, 1fr) pins the row to the board and hands the clipping back to
     each panel's own overflow, which is where it belongs. */
  grid-template-rows: minmax(0, 1fr);
  gap: var(--crm-board-pad);
  padding: var(--crm-board-pad);
  background: var(--crm-mist);
  height: var(--crm-board-h, 520px);
}

/* ---------- sidebar (Sidebar.module.css) ---------- */
/* Measured: at 14px/16px/32px the sidebar's content ran to 505.8px inside a
   482px content box, so the user chip's bottom corner was sliced flush against
   the board edge. 12px padding, 10px gap and a 30px nav row bring the content
   to 484px inside a 494px box at the 518px board height. overflow:hidden is
   the backstop, not the plan. */
.crm-ui__side {
  background: var(--crm-white);
  border-radius: var(--crm-radius);
  display: flex;
  flex-direction: column;
  padding: 12px;
  gap: 10px;
  min-width: 0;
  overflow: hidden;
}

.crm-ui__head-block { display: flex; flex-direction: column; gap: 4px; }

.crm-ui__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px;
}

/* The 32x32 slot the flying badge lands in. It stays empty on purpose: the
   badge IS the logo once it parks, and drawing a second one here would give
   the landing a double image. The box still holds its space so the wordmark
   beside it never shifts, and so the JS can measure the target while the
   badge is still out on the stage.

   The fallback (no JS, reduced motion, under the pin threshold) fills it with the real
   badge image instead, see .crm-ui__brand-mark img in crm-spotlight.css. */
.crm-ui__brand-mark {
  width: 32px;
  height: 32px;
  flex: none;
  display: block;
  border-radius: 8px;
}

.crm-ui__brand-name {
  font-weight: 700;
  font-size: var(--crm-text-sm);
  letter-spacing: var(--crm-tracking-tight);
  white-space: nowrap;
}

.crm-ui__tenant {
  margin: 0;
  padding: 0 8px;
  font-size: 11px;
  color: var(--crm-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.crm-ui__nav { display: flex; flex-direction: column; gap: 2px; }

.crm-ui__nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 30px;
  padding: 0 10px;
  border-radius: var(--crm-radius-sm);
  font-size: var(--crm-text-xs);
  font-weight: 600;
  color: var(--crm-ink);
}
.crm-ui__nav-item svg { width: 18px; height: 18px; flex: none; }
.crm-ui__nav-item.is-active { background: var(--crm-accent-subtle); color: var(--crm-accent-text); }

.crm-ui__spacer { flex: 1; }

/* UserChip.module.css */
.crm-ui__chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px;
  border-radius: var(--crm-radius-sm);
  border: 1px solid var(--crm-line);
}
.crm-ui__chip-avatar {
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: var(--crm-radius-pill);
  background: var(--crm-accent);
  color: var(--crm-accent-on);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 800;
}
.crm-ui__chip-meta { min-width: 0; }
.crm-ui__chip-meta strong { display: block; font-size: var(--crm-text-xs); font-weight: 700; }
.crm-ui__chip-meta span { display: block; font-size: 11px; color: var(--crm-muted); }

/* ---------- main panel ---------- */
.crm-ui__main {
  background: var(--crm-white);
  border-radius: var(--crm-radius);
  padding: var(--crm-board-pad);
  min-width: 0;
  display: grid;
  /* The pane's 300px minimum is the load-bearing number here: below it the
     bubbles, the composer and the Send control start getting clipped by the
     board's fixed height and overflow, and Step 04's whole payoff disappears.
     The conversations track is the one that yields. */
  grid-template-columns: minmax(0, var(--crm-convos-w, 304px)) minmax(300px, 1fr);
  grid-template-rows: minmax(0, 1fr);
  gap: 10px;
}

/* ---------- conversations panel (ConversationsPanel.module.css) ---------- */
.crm-ui__convos {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--crm-white);
  border: 1px solid var(--crm-line);
  border-radius: var(--crm-radius);
  overflow: hidden;
}

.crm-ui__convos-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 12px 16px;
  border-bottom: 1px solid var(--crm-line);
  min-height: 52px;
}
.crm-ui__convos-head h3 {
  margin: 0;
  flex: 1;
  font-size: var(--crm-text-sm);
  font-weight: 700;
  letter-spacing: var(--crm-tracking-tight);
}
.crm-ui__convos-count { font-size: 11px; font-weight: 600; color: var(--crm-accent-text); }
.crm-ui__convos-toggle { width: 22px; height: 22px; color: var(--crm-muted); flex: none; }

/* the four channel tiles, laid across the top of the list */
.crm-ui__channel-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--crm-line);
}
.crm-ui__tile {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--crm-radius-sm);
  background: var(--crm-cloud);
}
.crm-ui__tile svg { width: 20px; height: 20px; display: block; }
.crm-ui__tile-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 17px;
  height: 17px;
  padding: 0 5px;
  border-radius: var(--crm-radius-pill);
  background: var(--crm-accent);
  color: var(--crm-accent-on);
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px var(--crm-white);
}

.crm-ui__rows {
  list-style: none;
  margin: 0;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 0;
  overflow: hidden;
}

/* Icon led: the app mark is the leading element, no initials avatar. With four
   inboxes pooled into one list the first question a host asks is which app
   this arrived in. Straight from ConversationsPanel.tsx's header. */
.crm-ui__row {
  display: flex;
  gap: 12px;
  padding: 11px 12px;
  border-radius: var(--crm-radius-sm);
  color: var(--crm-ink);
}
.crm-ui__row.is-selected { background: var(--crm-accent-subtle); }

/* A rounded tile rather than a circle: these are app icons, and all four ship
   as a squircle on the platforms the host knows them from. */
.crm-ui__row-mark {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--crm-radius-sm);
  background: var(--crm-cloud);
}
.crm-ui__row-mark svg { width: 20px; height: 20px; display: block; }

.crm-ui__row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.crm-ui__row-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0; }
.crm-ui__row-name {
  font-size: var(--crm-text-xs);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crm-ui__row.is-unread .crm-ui__row-name { font-weight: 700; }
.crm-ui__row-time { flex: none; font-size: 11px; color: var(--crm-muted); }
.crm-ui__row-summary {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--crm-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crm-ui__row-foot { display: flex; align-items: center; gap: 8px; margin: 2px 0 0; }
.crm-ui__row-channel { font-size: 10.5px; font-weight: 600; color: var(--crm-muted); }
.crm-ui__pip {
  padding: 1px 6px;
  border-radius: var(--crm-radius-pill);
  background: var(--crm-accent);
  color: var(--crm-accent-on);
  font-size: 10px;
  font-weight: 700;
}

/* ---------- the conversation (InboxPanels.module.css) ---------- */
.crm-ui__pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  background: var(--crm-white);
  border: 1px solid var(--crm-line);
  border-radius: var(--crm-radius);
  overflow: hidden;
}

.crm-ui__pane-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--crm-line);
  flex: none;
}
.crm-ui__back { flex: none; width: 26px; height: 26px; color: var(--crm-muted); transform: rotate(180deg); }
.crm-ui__pane-avatar {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--crm-accent);
  color: var(--crm-accent-on);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
}
.crm-ui__pane-identity { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.crm-ui__pane-name { font-size: var(--crm-text-xs); font-weight: 700; }
.crm-ui__pane-stay { font-size: 11px; color: var(--crm-muted); }
.crm-ui__pane-channel {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--crm-muted);
  background: var(--crm-cloud);
  border-radius: var(--crm-radius-pill);
  padding: 5px 10px;
}

/* Channel as a coloured dot plus its name in text, never a vendor logo. That
   is InboxPanels.tsx's rule and it is the one place in this replica where a
   brand mark would be wrong. */
.crm-ui__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--crm-muted); flex: none; display: inline-block; }
.crm-ui__dot[data-channel="whatsapp"] { background: var(--crm-success); }
.crm-ui__dot[data-channel="telegram"] { background: var(--crm-signal-blue); }
.crm-ui__dot[data-channel="email"] { background: var(--crm-violet-bright); }

/* justify-content:flex-end, so when the pane is short - and it is, at every
   width below 1440, because the board height is fixed and a narrower composer
   wraps the reply onto more lines - the message that gets clipped is the
   OLDEST one at the top, never the newest at the bottom. Measured at 1280
   before this: the third and most recent bubble, the one the reply is
   answering, was the one cut off. Which is also how a real thread behaves. */
.crm-ui__messages {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 16px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 14px;
}
.crm-ui__bubble-row { display: flex; flex-direction: column; gap: 5px; max-width: 76%; }
.crm-ui__bubble-row[data-direction="outbound"] { align-self: flex-end; align-items: flex-end; }
.crm-ui__bubble {
  margin: 0;
  padding: 10px 13px;
  border-radius: var(--crm-radius-sm);
  font-size: var(--crm-text-xs);
  line-height: var(--crm-leading-body);
}
.crm-ui__bubble[data-direction="inbound"] {
  background: var(--crm-cloud);
  color: var(--crm-ink);
  border-bottom-left-radius: var(--crm-radius-xs);
}
.crm-ui__bubble[data-direction="outbound"] {
  background: var(--crm-accent);
  color: var(--crm-accent-on);
  border-bottom-right-radius: var(--crm-radius-xs);
}
.crm-ui__bubble-meta { display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--crm-muted); padding: 0 2px; }

/* ---------- composer ---------- */
.crm-ui__composer { flex: none; border-top: 1px solid var(--crm-line); padding: 10px 14px 12px; background: var(--crm-white); }
.crm-ui__composer-field {
  border: 1px solid var(--crm-accent-border);
  border-radius: var(--crm-radius-sm);
  background: var(--crm-accent-subtle);
  padding: 9px 11px;
  min-height: 56px;
}
.crm-ui__composer-text {
  margin: 0;
  font-size: var(--crm-text-xs);
  line-height: var(--crm-leading-body);
  color: var(--crm-ink);
  min-height: 20px;
}
/* The placeholder belongs on the SPAN that gets typed into, not the
   paragraph: the paragraph also holds the caret, so it is never :empty and the
   composer sat as a blank box for the whole first half of the section. */
.crm-ui__composer-text > [data-crm-type]:empty::before { content: "Reply on WhatsApp\2026"; color: var(--crm-muted); }
.crm-ui__caret {
  display: inline-block;
  width: 1.5px;
  height: 14px;
  background: var(--crm-accent);
  vertical-align: -2px;
  margin-left: 1px;
  opacity: 0;
}
/* The caret only exists while the reply is being typed. Outside that it is a
   blinking bar in a finished message, which reads as a bug. */
.crm-ui--typing .crm-ui__caret { opacity: 1; animation: crm-caret 1s steps(1) infinite; }
@keyframes crm-caret { 50% { opacity: 0; } }

.crm-ui__composer-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 9px; }
.crm-ui__send-via { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--crm-muted); }
.crm-ui__change { color: var(--crm-accent-text); font-weight: 600; }
.crm-ui__send {
  font-size: var(--crm-text-xs);
  font-weight: 600;
  padding: 7px 15px;
  border-radius: var(--crm-radius-sm);
  background: var(--crm-cloud);
  color: var(--crm-muted);
  transition: background .3s var(--ease, cubic-bezier(.22,1,.36,1)), color .3s var(--ease, cubic-bezier(.22,1,.36,1));
}
.crm-ui__send.is-lit { background: var(--crm-accent); color: var(--crm-accent-on); }


/* ---------- the pane's two states ----------
   The real app shows an EmptyState until a conversation is picked, so the
   walkthrough shows the same thing: the pane starts empty and the thread
   arrives in it. Both states are stacked in the same box rather than swapped,
   so the pane's own size never changes and the window cannot twitch when the
   thread opens. */
.crm-ui__pane { position: relative; }

.crm-ui__pane-empty {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 32px;
  text-align: center;
  background: var(--crm-white);
  border-radius: var(--crm-radius);
  pointer-events: none;
}
.crm-ui__pane-empty p:first-child { margin: 0; font-size: var(--crm-text-sm); font-weight: 700; }
.crm-ui__pane-empty p:last-child { margin: 0; max-width: 30ch; font-size: 12px; color: var(--crm-muted); }

.crm-ui__conv {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* The visually hidden description that carries the aria-hidden replica. */
.crm-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------
   The replica is a 1440px product shown inside an illustration whose width is
   a function of the viewport. Every step below was picked by measuring the
   resulting conversation pane, which must stay above 300px so the thread, the
   composer and Send all survive the board's fixed height:

     viewport   window   rail   convos   pane
     1440       980      224    304      390
     1366       970      196    280      432
     1280       884      196    280      346
     1180       824       60    248      454
     1024       668       60    248      298

   Under 1280 the rail collapses to the icon rail the real app already uses at
   its own narrow breakpoint, rather than the sidebar being deleted: the flying
   badge parks on .crm-ui__brand-mark, so that slot has to exist and stay 32px
   at every width the section pins at.
   --------------------------------------------------------------- */
@media (max-width: 1399px) {
  .crm-ui { --crm-rail-w: 196px; --crm-convos-w: 280px; }
}

@media (min-width: 900px) and (max-width: 1279px) {
  .crm-ui { --crm-rail-w: 60px; --crm-convos-w: 248px; }
  .crm-ui__side { padding: 12px 8px; align-items: stretch; }
  .crm-ui__brand { padding: 4px 0; justify-content: center; gap: 0; }
  .crm-ui__brand-name,
  .crm-ui__tenant { display: none; }
  /* font-size:0 rather than a wrapper span: the label is a bare text node next
     to the glyph, and the glyph carries explicit width/height attributes so it
     is unaffected. */
  .crm-ui__nav-item { justify-content: center; padding: 0; gap: 0; font-size: 0; }
  .crm-ui__chip { justify-content: center; padding: 6px 2px; }
  .crm-ui__chip-meta { display: none; }
}

/* ---------------------------------------------------------------
   Narrow. The pinned section never runs under 1024px, so this is the stacked
   fallback's job: keep every one of the four channels identifiable, drop the
   two-panel inbox to one column, and never scroll sideways.
   --------------------------------------------------------------- */
@media (max-width: 899px) {
  .crm-ui { border-radius: 20px; }
  .crm-ui__board { grid-template-columns: 1fr; height: auto; }
  .crm-ui__side { overflow: visible; }
  /* The rail is the app's own <700px behaviour: MobileNavDrawer takes over and
     the sidebar is gone. Keeping the brand row is what carries the handoff. */
  /* .crm-ui__side > .crm-ui__nav-item catches the bare "How it works" row,
     which is a SIBLING of .crm-ui__nav rather than a child of it and so
     survived the rail being dropped, sitting alone under the brand row. */
  .crm-ui__nav,
  .crm-ui__side > .crm-ui__nav-item,
  .crm-ui__tenant,
  .crm-ui__spacer,
  .crm-ui__chip { display: none; }
  .crm-ui__side { padding: 10px; }
  .crm-ui__main { grid-template-columns: minmax(0, 1fr); }
  .crm-ui__rows { overflow: visible; }
  .crm-ui__messages { overflow: visible; justify-content: flex-start; }
  .crm-ui__bubble-row { max-width: 88%; }
}

/* ============================================================
   THE REPLICA IN PORTRAIT
   ============================================================

   Under 1024px the section does not shrink the desktop window, it renders a
   phone. Same DOM, same nodes, same data-crm hooks - so the badge still parks
   on the real brand mark, the rows are still the real rows, and there is no
   second replica to keep in sync with this one.

   The rules: nothing scales, nothing is hidden that the animation drives, and
   the brand slot stays on screen because the whole payoff of the section is
   the badge landing in it.
   ============================================================ */

.crm-spot--phone .crm-ui {
  width: var(--crm-phone-w);
  /* 1:2. A phone that is not roughly 1:2 reads as a tablet and the "on your
     phone" claim goes with it. --crm-phone-w is defined on the section, so
     the device, the stage and the notification cards all derive from one
     number. */
  height: calc(var(--crm-phone-w) * 2);
  /* Flex, so the board takes exactly what the status bar leaves. As a grid
     child with height:100% it overran the shell by 18px and the composer's
     bottom edge was being sliced off by the shell's own overflow. */
  display: flex;
  flex-direction: column;
  border-radius: 34px;
  border: 7px solid #17151b;
  box-shadow: 0 30px 60px -18px rgb(25 23 28 / .45), 0 2px 6px rgb(25 23 28 / .12);
  overflow: hidden;
}

/* The browser chrome becomes a status bar. The dots and the URL are desktop
   furniture and say the wrong thing on a device. */
.crm-spot--phone .crm-ui__chrome {
  height: 26px;
  padding: 0 14px;
  background: var(--crm-white);
  border-bottom: 0;
  justify-content: center;
}
.crm-spot--phone .crm-ui__dots { display: none; }
.crm-spot--phone .crm-ui__url {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--crm-ink, #17151b);
}

.crm-spot--phone .crm-ui__board {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
  padding: 8px;
  /* The board carries a fixed desktop height. Left on, it rendered 498px of
     board inside a 616px phone and the bottom 87px was dead white. */
  flex: 1;
  min-height: 0;
}

/* The sidebar becomes a header strip. Everything in it that is navigation
   goes; the brand block stays, because that is where the badge lands. */
.crm-spot--phone .crm-ui__side {
  flex-direction: row;
  align-items: center;
  padding: 8px 10px;
  gap: 8px;
}
.crm-spot--phone .crm-ui__nav,
.crm-spot--phone .crm-ui__spacer,
.crm-spot--phone .crm-ui__chip,
.crm-spot--phone .crm-ui__tenant { display: none; }
.crm-spot--phone .crm-ui__head-block { flex-direction: row; align-items: center; flex: 1; }

.crm-spot--phone .crm-ui__main {
  position: relative;
  grid-template-columns: minmax(0, 1fr);
  padding: 0;
  gap: 0;
}

/* The list owns the screen. */
.crm-spot--phone .crm-ui__convos { border-radius: 18px; }

/* And the thread rides OVER it, the way a phone app pushes a detail view
   rather than sitting beside a list it has no room for. It is the same
   opacity channel the desktop drives - no new actor, no new band. */
/* The pane is the CONTAINER, and on desktop it is an opaque white card
   sitting beside the list. Laid over the list on a phone it hid the rows for
   the whole 0.58 -> 0.70 band, so the inbox resolved as an empty box and the
   "it lands in one list" beat had nothing to show. The container goes
   transparent and the thread itself - which is the node the driver actually
   fades - carries the surface. */
.crm-spot--phone .crm-ui__pane {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: transparent;
  border: 0;
  pointer-events: none;
}
.crm-spot--phone .crm-ui__conv {
  height: 100%;
  background: var(--crm-white);
  border: 1px solid var(--crm-line);
  border-radius: 18px;
  overflow: hidden;
}
.crm-spot--phone .crm-ui__pane-empty { display: none; }
/* The back chevron is dead furniture on desktop and the one control that
   makes the pushed view legible on a phone. */
.crm-spot--phone .crm-ui__back { display: block; }
/* Three bubbles plus a composer are taller than the pane on a phone, and
   with overflow visible the last bubble rendered straight through the
   composer. Clipped, and anchored to the bottom, so what gets cut is the
   OLDEST message - which is what a real thread does. */
.crm-spot--phone .crm-ui__messages {
  padding: 10px 12px;
  overflow: hidden;
  justify-content: flex-end;
}
.crm-spot--phone .crm-ui__composer { padding: 8px 12px 10px; }

/* The pane head was eating 122px of a 411px pane - the guest name wrapped to
   two lines because the channel pill was holding a third of the row - and the
   thread underneath was down to one visible message. The pill goes: every
   bubble already carries its own channel dot and the section's whole claim is
   that the channel stops mattering once it is in here. */
.crm-spot--phone .crm-ui__pane-head { padding: 8px 10px; gap: 8px; }
.crm-spot--phone .crm-ui__pane-channel { display: none; }
.crm-spot--phone .crm-ui__pane-avatar { width: 30px; height: 30px; font-size: 11px; }
.crm-spot--phone .crm-ui__pane-name { font-size: 13px; }
.crm-spot--phone .crm-ui__pane-stay { font-size: 10.5px; }
.crm-spot--phone .crm-ui__composer-foot { padding-top: 6px; }
.crm-spot--phone .crm-ui__bubble { font-size: 12.5px; }
.crm-spot--phone .crm-ui__bubble-meta { font-size: 10px; }
