/* The #message chat: visitors writing to Will, and Will's inbox.
   It follows the Mac inbox app (mac/): a native messaging layout in the site's colours and Fira Code.
   Ink bubbles for you, surface for them; ink marks unread and your own reaction.
   Built on the site's system (cv.html, "Screen: the system"): one 680px column, body 13/20,
   44px fields and buttons, 40px round buttons. Screen only; print keeps the CV. */
@media screen {
  body.in-chat {
    width: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
  }
  #view-message:not([hidden]) {
    display: block;
    position: fixed;
    z-index: 8;
    top: 64px;
    left: 0;
    right: 0;
    bottom: calc(42px + env(safe-area-inset-bottom));   /* room for the footer */
    overflow: hidden;
    background: var(--paper);
    border-top: 1px solid var(--line);
  }
  /* The site's footer, at the window's base under the chat */
  body.in-chat .site-foot { position: fixed; left: 0; right: 0; bottom: 0; margin: 0; padding: 12px 16px calc(16px + env(safe-area-inset-bottom)); font-size: 10px; line-height: 14px; }
  body.in-chat .site-foot .theme { display: none; }   /* Message keeps its one-line footer, centred, and has no switch: it is always dark */
  body.in-chat .site-foot { justify-content: center; text-align: center; }
  /* Until the app opens (in a browser: install, join, notifications, Will's sign-in) the footer is the pages' own, its
     line centred (no switch: Message is always dark); the chat makes room for it */
  body.in-chat.im-flow .site-foot { left: calc((100% - var(--col)) / 2); right: calc((100% - var(--col)) / 2); padding: 12px 0 calc(24px + env(safe-area-inset-bottom)); font-size: 12px; line-height: 16px; justify-content: center; text-align: center; }   /* the column's edges and the pages' own spacing, so the switch sits where it does on every page */
  body.in-chat.im-flow #view-message:not([hidden]) { bottom: calc(64px + env(safe-area-inset-bottom)); }
}
@media screen and (max-width: 640px) {
  #view-message:not([hidden]) { top: 56px; bottom: calc(34px + env(safe-area-inset-bottom)); }
  body.in-chat .site-foot { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  body.in-chat.im-flow .site-foot { padding-bottom: calc(16px + env(safe-area-inset-bottom)); font-size: 10px; line-height: 14px; }
  body.in-chat.im-flow #view-message:not([hidden]) { bottom: calc(56px + env(safe-area-inset-bottom)); }
}
/* The installed app has neither the site's bar nor its footer */
@media screen and (display-mode: standalone) {
  .site-nav, body.in-chat .site-foot { display: none !important; }
  #view-message:not([hidden]) { top: 0 !important; bottom: 0 !important; }
}
html.standalone .site-nav, html.standalone body.in-chat .site-foot { display: none !important; }
/* Signed in, in a browser too: no site bar over the app (its header carries Will, the call and the settings) */
body.in-chat.im-in .site-nav, body.in-chat.im-in .site-foot { display: none !important; }   /* nor the footer and llms.txt */
body.in-chat.im-in #view-message:not([hidden]) { top: 0 !important; bottom: 0 !important; }   /* the message bar sits at the window's base */
html.standalone #view-message:not([hidden]) { top: 0 !important; bottom: 0 !important; }

/* The site's tokens, under the chat's own names */
.im {
  --im-muted: var(--ink-2);
  --im-soft: var(--surface);                /* their bubbles, round buttons */
  --im-line: var(--line);
  --im-field: var(--ink-3);                 /* field borders: 3:1 against paper */
  --im-on-sheen: var(--paper);
  --im-danger: var(--danger);
  --im-avatar: var(--avatar);
  --im-drop: rgba(17, 17, 16, .28);         /* shadows: soft on the light set, black on the dark */
  --im-veil: rgba(239, 239, 239, .6);       /* what dims the thread behind an open menu */
  /* the sliced chrome bezels: dark chrome on the light set, bright on the dark */
  --im-btn-bezel: url(/media/chrome/dark/btn-bezel.png);
  --im-card-bezel: url(/media/chrome/dark/card-bezel.png);
  --im-dd-bezel: url(/media/chrome/dark/dd-bezel.png);
  --im-dd-bezel-20: url(/media/chrome/dark/dd-bezel-r20.png);   /* the message bar's round ends */
  --im-dd-bezel-32: url(/media/chrome/dark/dd-bezel-r32.png);   /* the menu's chrome re-cut at 32 and 38px corners (scripts/build-dd-bezel.py) */
  --im-dd-bezel-38: url(/media/chrome/dark/dd-bezel-r38.png);
  --im-col: max(16px, calc((100% - 680px) / 2));   /* the thread sits in the site's column */
  height: 100%;
  position: relative;
  color: var(--ink);
  background: var(--paper);
  font-size: 13px;
  line-height: 20px;
}

[data-theme="dark"] .im { --im-drop: #000; --im-veil: color-mix(in srgb, var(--paper) 72%, transparent); --im-btn-bezel: url(/media/chrome/btn-bezel.png); --im-card-bezel: url(/media/chrome/card-bezel.png); --im-dd-bezel: url(/media/chrome/dd-bezel.png); --im-dd-bezel-20: url(/media/chrome/dd-bezel-r20.png); --im-dd-bezel-32: url(/media/chrome/dd-bezel-r32.png); --im-dd-bezel-38: url(/media/chrome/dd-bezel-r38.png); }
:where(.im) button { font: inherit; color: inherit; }   /* zero specificity: component classes below win */
.im :focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.im svg { display: block; flex: none; }

/* ---------- Layout ---------- */
.im-app { height: 100%; display: flex; min-width: 0; }
.im-list, .im-thread { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--paper); }
.im-list { width: 100%; }
.im-thread { flex: 1; position: relative; }

.im-visitor .im-list { display: none; }
.im-visitor .im-thread { display: flex; width: 100%; }
.im-visitor .im-back { display: none; }

.im-operator .im-thread { display: none; }
.im-operator.show-thread .im-list { display: none; }
.im-operator.show-thread .im-thread { display: flex; width: 100%; }
@media (min-width: 780px) {
  .im-operator .im-list { display: flex !important; width: 320px; flex: none; border-right: 1px solid var(--im-line); }
  .im-operator .im-thread,
  .im-operator.show-thread .im-thread { display: flex !important; }
  .im-operator .im-back { display: none; }
}

/* ---------- Thread header ---------- */
.im-thread-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 64px;
  padding: 12px var(--im-col);
  border-bottom: 1px solid var(--im-line);
}
html.standalone .im-thread-bar, html.standalone .im-list-bar { padding-top: calc(8px + env(safe-area-inset-top)); }
.im-back {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--im-muted);
  font-size: 12px;
}
.im-back:hover { color: var(--ink); }
.im-peer { display: flex; align-items: center; gap: 12px; min-width: 0; }
.im-peer-text { display: flex; flex-direction: column; min-width: 0; }
.im-peer-name { font-size: 13px; line-height: 20px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.im-peer-sub { font-size: 12px; line-height: 16px; color: var(--im-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.im-avatar {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--im-avatar);
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
  grid-row: 1 / span 2;
}
.im-avatar.sm { width: 32px; height: 32px; font-size: 11px; grid-row: auto; }
.im-avatar.xl { width: 64px; height: 64px; font-size: 24px; }

/* ---------- Conversation ---------- */
.im-log {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  padding: 12px var(--im-col) 12px;
  scrollbar-width: thin;
}

/* The top of every thread: who this is and how it is sealed */
.im-intro { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 16px 0 8px; text-align: center; }
.im-intro .im-avatar.xl { margin-bottom: 8px; }
.im-intro-name { font-size: 16px; line-height: 24px; font-weight: 600; }
.im-intro-sub { font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-intro-lock { margin-top: 8px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
/* Icons sit in the line of text, so they stay beside the words when it wraps and centres */
.im-intro-lock svg, .im-foot svg { display: inline-block; margin-right: 8px; vertical-align: -1px; }
.im-log > .im-intro:only-child { margin: auto 0; }   /* a visitor's welcome sits in the middle of the empty thread */

/* A visitor's settings button, top right of the thread, in its own row so nothing scrolls under it */
.im-tools { flex: none; display: flex; justify-content: flex-end; padding: 12px var(--im-col) 0; }

.im-stamp { margin: 24px 0 8px; text-align: center; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-stamp b { font-weight: 600; }

/* A bubble: a pill for one line, a rounded rectangle for more */
.im-bubble {
  max-width: min(78%, 440px);
  margin-top: 4px;
  padding: 8px 12px;
  border-radius: 18px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.im-bubble.mine { align-self: flex-end; background: linear-gradient(180deg, var(--ink), color-mix(in srgb, var(--ink) 82%, var(--paper))); color: var(--paper); }
.im-bubble.theirs { align-self: flex-start; background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 16%, var(--paper)), color-mix(in srgb, var(--ink) 8%, var(--paper))); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 9%, transparent); color: var(--ink); }
.im-bubble.group-start { margin-top: 20px; }
.im-bubble.sealed { background: transparent; color: var(--im-muted); box-shadow: inset 0 0 0 1px var(--im-line); font-size: 12px; }

/* One message row: the bubble, its reactions, and the react and reply buttons beside it */
.im-msg { display: flex; flex-direction: column; margin-top: 4px; border-radius: 20px; }
.im-msg.group-start { margin-top: 20px; }   /* a new sender (or a pause) opens a clear gap; within a run, 4px */
.im-msg.reacted { padding-bottom: 18px; }
.im-msg .im-bubble { max-width: 100%; margin-top: 0; }
.im-line { position: relative; display: flex; align-items: center; gap: 6px; }
.im-msg.mine .im-line { flex-direction: row-reverse; }
.im-hold { position: relative; min-width: 0; max-width: min(440px, calc(100% - 80px)); display: flex; flex-direction: column; gap: 4px; }
.im-msg.mine .im-hold { align-items: flex-end; }
.im-msg.theirs .im-hold { align-items: flex-start; }
.im-text { white-space: pre-wrap; }
.im-bubble.legacy { font-style: normal; }

/* One to three emoji and nothing else are shown large, without a bubble */
.im-bubble.emoji { padding: 0; background: none; font-size: 44px; line-height: 52px; }

/* Replies: the answered message at the top of the bubble. Clicking it jumps there. */
.im-bubble.quoted { display: flex; flex-direction: column; gap: 4px; padding: 4px 4px 8px; }
.im-bubble.quoted .im-text { padding: 0 8px; }
.im-quote {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  max-width: 100%;
  padding: 8px 12px 8px 20px;
  border: 0;
  border-radius: 14px;
  background: var(--paper);
  color: var(--ink-2);
  text-align: left;
  white-space: normal;
  cursor: pointer;
}
.im-quote::before { content: ""; position: absolute; left: 8px; top: 8px; bottom: 8px; width: 3px; border-radius: 2px; background: var(--ink); }
.im-bubble.mine .im-quote { background: color-mix(in srgb, var(--paper) 14%, transparent); color: color-mix(in srgb, var(--paper) 85%, transparent); }
.im-bubble.mine .im-quote::before { background: color-mix(in srgb, var(--paper) 55%, transparent); }
.im-quote-who { font-size: 12px; line-height: 16px; font-weight: 600; }
.im-quote-text { font-size: 12px; line-height: 16px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow-wrap: anywhere; }
.im-quote-card { padding: 4px; border-radius: 14px; background: var(--im-soft); max-width: 100%; }

/* Images and GIFs: the picture itself, rounded, with no bubble */
.im-bubble.media { position: relative; display: block; width: fit-content; padding: 0; line-height: 0; background: none; border-radius: 16px; overflow: hidden; }
.im-bubble.media img { display: block; max-width: min(280px, 100%); max-height: 320px; width: auto; height: auto; }
.im-badge { position: absolute; right: 8px; bottom: 8px; padding: 2px 5px; border-radius: 4px; background: rgba(0, 0, 0, .55); color: #fff; font-size: 10px; line-height: 14px; font-weight: 600; }

/* Reactions: one chip under the bubble's inner corner. An ink ring marks your own. */
.im-chip {
  position: absolute;
  bottom: -15px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 24px;
  padding: 0 8px;
  border: 1px solid var(--im-line);
  border-radius: 12px;
  background: var(--paper);
  cursor: pointer;
}
.im-msg.mine .im-chip { left: 12px; }
.im-msg.theirs .im-chip { right: 12px; }
.im-chip.mine { border: 1.5px solid var(--ink); }
.im-chip:disabled { cursor: default; }
.im-chip-emoji, .im-pick { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; }
.im-chip-emoji { font-size: 13px; line-height: 1; }
.im-chip-count { font-size: 12px; font-weight: 600; color: var(--im-muted); }

/* React and reply: shown beside the bubble while the pointer is over it (a tap on touch screens) */
.im-acts { flex: none; display: flex; gap: 4px; opacity: 0; pointer-events: none; }
.im-line:hover .im-acts, .im-line:focus-within .im-acts, .im-msg.picking .im-acts, .im-msg.active .im-acts { opacity: 1; pointer-events: auto; }
@media (hover: none) {
  .im-line:hover .im-acts { opacity: 0; pointer-events: none; }
  .im-msg.picking .im-acts, .im-msg.active .im-acts { opacity: 1; pointer-events: auto; }
}
.im-act {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--im-soft);
  color: var(--ink-2);
  cursor: pointer;
}
.im-act:hover, .im-msg.picking .im-react { color: var(--ink); }
.im-picker {
  position: absolute;
  bottom: calc(100% + 6px);
  z-index: 3;
  display: flex;
  gap: 2px;
  padding: 6px;
  border: 1px solid var(--im-line);
  border-radius: 26px;
  background: var(--paper);
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, .3);
}
.im-msg.mine .im-picker { right: 0; }
.im-msg.theirs .im-picker { left: 0; }
.im-pick { width: 36px; height: 36px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 50%; background: none; font-size: 22px; line-height: 1; cursor: pointer; }
.im-pick:hover { background: var(--im-soft); }
.im-pick.on { background: var(--line); }

/* The answered message lights up when a quote jumps to it */
.im-msg.flash .im-hold { border-radius: 20px; animation: im-flash 1.6s ease-out both; }
@keyframes im-flash { 0%, 40% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--ink) 12%, transparent); background: color-mix(in srgb, var(--ink) 12%, transparent); } 100% { box-shadow: 0 0 0 5px transparent; background: transparent; } }

.im-status { align-self: flex-end; margin-top: 4px; padding-right: 4px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-status.bad { color: var(--im-danger); }
.im-ticks { display: flex; padding-right: 2px; }
.im-ticks svg { display: block; }

/* ---------- Composer ---------- */
/* The dock under a thread: the reply bar, then one 40px line of attach, field and send */
.im-dock {
  flex: none;
  padding: 8px var(--im-col) calc(16px + env(safe-area-inset-bottom));
}
/* The window's ground is the site's own: in dark mode (always, here) the grained black every page lies on (cv.html,
   body::before), outside the app as well as before it opens; the chat's layers above it stay clear */
html.chat body { background: transparent; }
body.im-in .im-veil { background: rgba(0, 0, 0, .72); }   /* so the menu's veil darkens the black rather than greying it */
html.chat body.in-chat .site-nav::before { background: transparent; -webkit-backdrop-filter: none; backdrop-filter: none; }   /* the site's bar lies on the ground too, not on a grey frost */
/* Signed in (never on the join screens), a visitor's chat lies on the site's one black (--paper, #0e0e0f) with a fine film grain that moves very slightly: the
   speckle jumps between a few places about ten times a second, like film, so it lives without drifting. Only the
   grain's position animates (the chrome side lines on this layer stay still). On phones it fills the screen under the
   header; on wider screens it is only the conversation's width, with a chrome line down each side. Reduced motion
   keeps it still. (It replaced the CLEAVS monogram, which Will took out.) */
/* Under the grain: a shade below the site's black, so grain and ground together average the same #0e0e0f as every
   other page (the grain's light speckle lifts it about four steps) */
html.chat { --im-ground: #0a0a0b; }
html.chat { --im-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .05 -.01'/></filter><rect width='180' height='180' filter='url(%23g)'/></svg>"); }
body.im-in .im-visitor .im-thread::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--im-grain) var(--im-ground); background-size: 180px 180px; pointer-events: none; animation: im-grain 1s steps(1) infinite; }
@keyframes im-grain {
  0% { background-position: 0 0; }
  12.5% { background-position: -47px 31px; }
  25% { background-position: 63px -18px; }
  37.5% { background-position: -22px -71px; }
  50% { background-position: 88px 54px; }
  62.5% { background-position: -79px -9px; }
  75% { background-position: 15px 92px; }
  87.5% { background-position: 51px -57px; }
}
@media (prefers-reduced-motion: reduce) { body.im-in .im-visitor .im-thread::before, .im-visitor .im-apphead::after { animation: none; } }
@media (min-width: 720px) {
  body.im-in .im-visitor .im-thread::before, body.im-in .im-visitor .im-apphead::before {
    left: calc(var(--im-col) - 16px); right: calc(var(--im-col) - 16px);
    border-style: solid; border-width: 0 20.1px; border-color: transparent;
    border-image: var(--im-card-bezel) 81 / 0 20.1px / 0 4.1px stretch;   /* the cards' chrome, sides only */
  }
  /* The lines carry on up through the header to the window's top (under the header's contents and its open menu) */
  body.im-in .im-visitor .im-apphead::before { content: ""; position: absolute; top: 0; bottom: 0; z-index: -1; pointer-events: none; }
  /* With the menu down they fade with the rest of the lines (those lie under the veil, the header's above it), at the veil's
     strength and pace, and come back as it closes */
  body.im-in .im-visitor .im-apphead.open::before { opacity: .28; animation: im-rails-dim .4s ease backwards; transition: opacity .38s ease; }   /* the header is redrawn open, so it fades in by animation */
  body.im-in .im-visitor .im-apphead.open:has(.im-dd.im-out)::before { opacity: 1; }
  @keyframes im-rails-dim { from { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) { body.im-in .im-visitor .im-apphead.open::before { animation: none; transition: none; } }
}
html.chat #view-message:not([hidden]), html.chat .im, html.chat .im-thread { background: transparent; }
html.chat #view-message:not([hidden]) { border-top: 0; }
/* the call screen keeps its own solid ground over the chat */
html.chat .im.im-call { background: var(--paper); }
/* The composer is one lit card: attach, field and send, with a highlight from below right, ringed in chrome like the
   menu card (the same render, its ends re-cut to the bar's 20px radius) */
.im-dock .im-compose {
  align-items: center;
  padding: 4px;
  border-radius: 20px;
  border-style: solid; border-width: 0; border-color: transparent;
  border-image: var(--im-dd-bezel-20) 96 / 24px / 4.1px stretch;
  background:
    radial-gradient(140% 180% at 78% 130%, color-mix(in srgb, var(--ink) 26%, transparent), transparent 55%),
    linear-gradient(180deg, color-mix(in srgb, var(--ink) 13%, var(--paper)), color-mix(in srgb, var(--ink) 6%, var(--paper)));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 16px 40px -16px var(--im-drop);
  transition: box-shadow .25s ease;
}
.im-dock .im-attach { width: 32px; height: 32px; }
/* + and GIF are solid, machined like the night switch's knob: spun metal (light, or dark steel in dark mode) with a soft
   highlight above, the symbol engraved into the face (a groove: see-through darker metal, a bright lip below, a faint
   shadow above). In their chrome rings. The call and More buttons above the thread are the same knobs (.im-knob) at 40px, and the call screen's at 68px */
:is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob) {
  color: rgba(38, 38, 36, .62);
  background: radial-gradient(circle at 50% 30%, rgba(255, 255, 255, .75), rgba(255, 255, 255, 0) 60%), conic-gradient(from 20deg, #f7f7f5, #bfbfbb, #ededea, #a9a9a5, #f7f7f5, #c8c8c4, #efefec, #b3b3af, #f7f7f5);
  box-shadow: 0 1px 3px rgba(17, 17, 16, .35);
  text-shadow: 0 .6px 0 rgba(255, 255, 255, .95), 0 -.5px 0 rgba(0, 0, 0, .28);
  transition: transform .15s ease, filter .25s ease, box-shadow .25s ease, color .25s ease, text-shadow .25s ease;
}
:is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob) svg { filter: drop-shadow(0 .6px 0 rgba(255, 255, 255, .95)) drop-shadow(0 -.5px 0 rgba(0, 0, 0, .28)); }
[data-theme="dark"] :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob) {
  color: rgba(0, 0, 0, .62);
  background: radial-gradient(circle at 50% 30%, rgba(255, 255, 255, .18), rgba(255, 255, 255, 0) 60%), conic-gradient(from 20deg, #5d5f63, #2c2d30, #4e5054, #232426, #5d5f63, #35363a, #56585c, #28292c, #5d5f63);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .6);
  text-shadow: 0 .6px 0 rgba(255, 255, 255, .28), 0 -.5px 0 rgba(0, 0, 0, .55);
}
[data-theme="dark"] :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob) svg { filter: drop-shadow(0 .6px 0 rgba(255, 255, 255, .28)) drop-shadow(0 -.5px 0 rgba(0, 0, 0, .55)); }
/* On hover the engraving lights from inside, like an LED set into the metal: the groove itself turns to light, sharp
   edged, with no glow spilling onto the face, which stays as it is. Pointers only, so a tap never leaves one lit */
@media (hover: hover) {
  :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob):hover { color: #fff; text-shadow: 0 -.5px 0 rgba(0, 0, 0, .55), 0 .5px 0 rgba(0, 0, 0, .3), .5px 0 0 rgba(0, 0, 0, .4), -.5px 0 0 rgba(0, 0, 0, .4); }   /* on light metal a hard dark rim is what shows the slot lit */
  :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob):hover svg { filter: drop-shadow(0 -.5px 0 rgba(0, 0, 0, .55)) drop-shadow(0 .5px 0 rgba(0, 0, 0, .3)) drop-shadow(.5px 0 0 rgba(0, 0, 0, .25)) drop-shadow(-.5px 0 0 rgba(0, 0, 0, .25)); }
  [data-theme="dark"] :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob):hover { color: #fff; text-shadow: 0 .6px 0 rgba(255, 255, 255, .28), 0 -.5px 0 rgba(0, 0, 0, .55); }
  [data-theme="dark"] :is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob):hover svg { filter: drop-shadow(0 .6px 0 rgba(255, 255, 255, .28)) drop-shadow(0 -.5px 0 rgba(0, 0, 0, .55)); }
  .im-icon-btn.im-call-btn.im-knob:hover, [data-theme="dark"] .im-icon-btn.im-call-btn.im-knob:hover { color: #3ee06a; }   /* the call lights green */
}
:is(.im-dock .im-attach, .im-icon-btn.im-knob, .im-call-r.im-knob):active { transform: scale(.94); }
.im-dock .im-pillbox { min-height: 32px; align-items: center; border: 0; background: transparent; padding: 0 0 0 8px; }   /* the bar stays one standard 40px line */
.im-dock .im-pillbox .im-input { height: 32px; }
.im-dock .im-pillbox .im-send { margin-bottom: 0; }
.im-dock .im-compose:focus-within { box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 24%, transparent), inset 0 0 0 1px color-mix(in srgb, var(--ink) 10%, transparent), 0 16px 40px -16px var(--im-drop); }   /* typing: the chrome is the edge, so the card lights from within */
.im-dock .im-pillbox:focus-within { border: 0; }
.im-dock .im-send.on { background: linear-gradient(180deg, var(--ink), color-mix(in srgb, var(--ink) 82%, var(--paper))); }
.im-compose {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 12px var(--im-col) calc(16px + env(safe-area-inset-bottom));
}
.im-dock .im-compose { padding: 4px; }   /* as on the canvas: attach, field and send sit 4px inside the lit card, its ring clear of the edge */
.im-input {
  flex: 1;
  min-width: 0;
  height: 40px;
  padding: 0 16px;
  border: 1px solid var(--im-field);
  border-radius: 20px;
  background: var(--paper);
  color: var(--ink);
  font: 13px "FiraWeb", "FiraCV", monospace;
  outline: none;
}
.im-input:focus { border-color: var(--ink); }
.im-input::placeholder { color: var(--im-muted); }
.im-send {
  width: 40px;
  height: 40px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--im-soft);
  color: var(--im-muted);
  cursor: default;
}
.im-send.on { background: var(--ink); color: var(--paper); cursor: pointer; }
.im-attach {
  flex: none;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--im-soft);
  color: var(--ink-2);
  cursor: pointer;
}
.im-attach:hover { color: var(--ink); }
/* GIFs: a GIF button beside +, and above the message bar a card that searches GIPHY (through cleavs.com). Trending first;
   type to search. Three columns of GIFs at their own heights, rounded 12px, more as you scroll; tap one and it is sent,
   sealed like any image. GIPHY is credited at the card's foot */
.im-gif-btn { font: 600 10px/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0; }
/* the GIF button while the picker is open: the knob pressed in, its face a shade darker and shadowed from above */
.im-dock .im-gif-btn.on, [data-theme="dark"] .im-dock .im-gif-btn.on, .im-icon-btn.im-knob[aria-expanded="true"], [data-theme="dark"] .im-icon-btn.im-knob[aria-expanded="true"] { filter: brightness(.82); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .55), 0 0 0 rgba(0, 0, 0, 0); }
.im-gifs { position: relative; box-sizing: border-box; margin: 0 0 12px; padding: 12px; border-radius: 16px; background: var(--paper); border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch; box-shadow: 0 24px 60px -24px var(--im-drop); animation: im-gifs-in .35s cubic-bezier(.16, 1, .3, 1) both; }
@keyframes im-gifs-in { from { opacity: 0; transform: translateY(8px); } }
/* The search field has the message bar's finish: a lit pill with a highlight from below right and a fine edge that brightens
   while you type in it (no browser outline) */
.im-gifs-search { height: 36px; display: flex; align-items: center; gap: 8px; padding: 0 14px; border-radius: 18px; color: var(--im-muted); cursor: text;
  background:
    radial-gradient(140% 180% at 78% 130%, color-mix(in srgb, var(--ink) 22%, transparent), transparent 55%),
    linear-gradient(180deg, color-mix(in srgb, var(--ink) 13%, var(--paper)), color-mix(in srgb, var(--ink) 6%, var(--paper)));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 0 0 1px color-mix(in srgb, var(--ink) 9%, transparent);
  transition: box-shadow .25s ease, color .25s ease; }
.im-gifs-search:focus-within { color: var(--ink); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 0 0 1px color-mix(in srgb, var(--ink) 30%, transparent); }
.im-gifs-search input:focus-visible { outline: none; }
.im-gifs-search input::placeholder { color: var(--im-muted); }
.im-gifs-search input { flex: 1; min-width: 0; border: 0; padding: 0; background: transparent; color: var(--ink); font: 16px/24px "Fira Code", ui-monospace, monospace; outline: none; }   /* 16px: iOS does not zoom */
@media (hover: hover) { .im-gifs-search input { font-size: 13px; line-height: 20px; } }
.im-gifs-grid { height: min(300px, 38vh); margin-top: 12px; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
.im-gifs-cols { columns: 3; column-gap: 6px; }
.im-gifs-grid::-webkit-scrollbar { display: none; }
.im-gif { display: block; width: 100%; margin: 0 0 6px; padding: 0; border: 0; border-radius: 12px; overflow: hidden; break-inside: avoid; background: color-mix(in srgb, var(--ink) 10%, var(--paper)); cursor: pointer; transition: opacity .25s ease, transform .25s cubic-bezier(.16, 1, .3, 1); }
.im-gif img { display: block; width: 100%; height: 100%; object-fit: cover; }
@media (hover: hover) { .im-gifs-grid:hover .im-gif:not(:hover) { opacity: .6; } }
.im-gif:active { transform: scale(.97); }
.im-gif.wait { height: 88px; cursor: default; background: linear-gradient(105deg, color-mix(in srgb, var(--ink) 8%, var(--paper)) 40%, color-mix(in srgb, var(--ink) 14%, var(--paper)) 50%, color-mix(in srgb, var(--ink) 8%, var(--paper)) 60%) 100% 0 / 300% 100% no-repeat; animation: im-gif-seek 1.6s cubic-bezier(.65, 0, .35, 1) infinite; }
.im-gif.wait:nth-child(3n) { height: 120px; }
.im-gif.wait:nth-child(4n+1) { height: 70px; }
@keyframes im-gif-seek { to { background-position: 0 0; } }
.im-gifs-note { margin: 0; padding: 24px 8px; text-align: center; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-gifs-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; font-size: 11px; line-height: 16px; color: var(--ink-3); }
.im-gifs-foot b { color: var(--im-muted); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .im-gifs, .im-gif.wait { animation: none; } .im-gif { transition: none; } }
.im-pillbox {
  flex: 1;
  min-width: 0;
  min-height: 40px;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 0 16px;
  border: 1px solid var(--im-field);
  border-radius: 20px;
  background: var(--paper);
}
.im-pillbox.ready { padding-right: 3px; }
.im-pillbox:focus-within { border-color: var(--ink); }
.im-pillbox .im-input { height: 38px; padding: 0; border: 0; border-radius: 0; background: transparent; }
.im-pillbox .im-send { width: 32px; height: 32px; margin-bottom: 2px; display: none; }
.im-pillbox .im-send.on { display: grid; }
.im-hint { margin-top: 8px; padding-left: 48px; color: var(--im-muted); font-size: 12px; line-height: 16px; }
@media (pointer: coarse) { .im-hint { display: none; } }

/* Above the message field while a reply is being written */
.im-replybar { display: flex; align-items: center; gap: 8px; margin: 0 0 8px 48px; padding-left: 12px; position: relative; min-width: 0; }
.im-replybar::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; border-radius: 2px; background: var(--ink); }
.im-replybar-text { flex: 1; min-width: 0; }
.im-replybar .im-quote-text { display: block; color: var(--im-muted); white-space: nowrap; text-overflow: ellipsis; }
.im-replybar-x { flex: none; display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 0; background: none; color: var(--im-muted); cursor: pointer; }
.im-replybar-x:hover { color: var(--ink); }

/* ---------- Inbox list (Will only), like the Mac app's sidebar ---------- */
.im-list-bar {
  flex: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 64px;
  padding: 12px 12px 12px 24px;
}
.im-list-title { font-size: 11px; line-height: 16px; font-weight: 600; }
.im-icon-btn {
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: var(--im-soft);
  color: var(--ink);
  display: grid;
  place-items: center;
  padding: 0;
  cursor: pointer;
}
.im-search {
  margin: 0 12px 8px;
  height: 36px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  border-radius: 8px;
  background: var(--im-soft);
  color: var(--im-muted);
}
.im-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: 12px "FiraWeb", "FiraCV", monospace;
  outline: none;
}
.im-search input::placeholder { color: var(--im-muted); }
.im-rows { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 12px 24px; }
.im-row {
  width: 100%;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  column-gap: 12px;
  row-gap: 0;
  align-items: center;
  margin-bottom: 4px;
  padding: 8px 12px;
  border: 0;
  border-radius: 12px;
  background: transparent;
  text-align: left;
  cursor: pointer;
}
.im-row:hover { background: var(--im-soft); }
.im-row.selected { background: var(--ink); color: var(--paper); }
.im-top, .im-bot { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.im-top { grid-column: 2; grid-row: 1; }
.im-bot { grid-column: 2; grid-row: 2; align-items: center; }
.im-name { font-size: 13px; line-height: 20px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.im-time { margin-left: auto; flex: none; font-size: 12px; color: var(--im-muted); }
.im-preview { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--im-muted); font-size: 12px; line-height: 16px; }
.im-row.unread .im-preview, .im-row.unread .im-time { color: var(--ink); }
.im-row.selected .im-time { color: color-mix(in srgb, var(--paper) 70%, transparent); }
.im-row.selected .im-preview { color: color-mix(in srgb, var(--paper) 75%, transparent); }
.im-count { flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; display: grid; place-items: center; background: var(--ink); color: var(--im-on-sheen); font-size: 11px; line-height: 1; font-weight: 600; }

.im-blank { flex: 1; display: grid; place-items: center; padding: 24px; color: var(--im-muted); font-size: 13px; text-align: center; }
.im-note {
  margin: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--im-soft);
  color: var(--ink);
  font-size: 12px;
  line-height: 16px;
}
.im-thread > .im-note { margin: 12px var(--im-col) 14px; }
.im-note a, .im-note button { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line); transition: text-decoration-color .25s ease; }
.im-note a:hover, .im-note button:hover { text-decoration-color: var(--ink); }
.im-note button { display: inline; border: 0; background: none; padding: 0; cursor: pointer; font-weight: 600; }

/* ---------- Sign-in and joining: laid out like the Mac app's sign-in window ---------- */
.im-join .im-card-sub { margin-top: 0; }
.im-join .im-card-title + .im-card-sub { margin-top: 8px; }
.im-join .im-card-sub + .im-card-form { margin-top: 32px; }
.im-step-acts { width: 100%; display: flex; flex-direction: column; gap: 12px; }
.im-step-acts .im-pill, .im-step-acts .im-pill.secondary { margin-top: 0; }   /* the stack's own 12px gap spaces them */
.im-card-form .im-step-acts { margin-top: 12px; }   /* 24px under the form, as on the canvas; phones pin it to the foot */
.im-step-acts .im-link { align-self: center; }
.im-card > .im-card-form:first-child { margin-top: 0; }
.im-spec { margin-bottom: 16px; font-size: 12px; line-height: 16px; color: var(--im-muted); cursor: default; }
.im-spec svg { display: inline-block; margin-right: 8px; vertical-align: -1px; }
.im-spec + .im-card-form { margin-top: 0; }   /* joining starts at its line of text: no tile or title */
.im-lock {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px calc(40px + env(safe-area-inset-bottom));
  overflow-y: auto;
}
.im-lock .im-back { position: absolute; top: 12px; left: var(--im-col); min-height: 40px; }
.im-log.im-join { justify-content: center; align-items: center; padding-top: 48px; padding-bottom: 48px; }
/* Phones, as native apps do: the content sits in the upper middle and the screen's action is pinned to the foot,
   in the same place on every step. Forms (name and email, code) keep their button under the fields, clear of the keyboard. */
@media screen and (max-width: 640px) {
  .im-log.im-join { justify-content: flex-start; padding-top: 56px; padding-bottom: calc(16px + env(safe-area-inset-bottom)); }
  .im-join .im-card { flex: 1 0 auto; margin: 0 auto; }
  .im-join .im-anchor { margin-top: auto !important; }
  .im-join .im-card-form.im-grow { flex: 1 0 auto; }
  /* with the keyboard up, the foot of the screen is the top of the keyboard */
  .im-log.im-join { padding-bottom: calc(16px + max(var(--kb, 0px), env(safe-area-inset-bottom))); }
  .im-join .im-anchor + .im-link.im-stay { margin-top: 4px; }
  html.standalone .im-log.im-join { padding-top: 40px; }   /* under the app's header */
}
.im-card { width: min(360px, 100%); margin: auto 0; display: flex; flex-direction: column; align-items: center; text-align: center; }
.im-tile { width: 56px; height: 56px; display: grid; place-items: center; border-radius: 12px; background: var(--ink); color: var(--paper); }
.im-card-title { margin-top: 16px; font-size: 20px; line-height: 28px; font-weight: 600; }
.im-card-sub { margin-top: 8px; font-size: 13px; line-height: 20px; color: var(--im-muted); }
.im-card-form { width: 100%; display: flex; flex-direction: column; gap: 12px; margin-top: 24px; }
.im-field {
  width: 100%;
  height: 44px;
  padding: 0 12px;
  border: 1px solid var(--im-field);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font: 13px "FiraWeb", "FiraCV", monospace;
  outline: none;
}
.im-field::placeholder { color: var(--im-muted); }
.im-field:focus { border-color: var(--ink); box-shadow: 0 0 0 3px var(--line); }
.im-code { letter-spacing: .5em; text-indent: .5em; font-size: 20px; text-align: center; }
.im-pill {
  height: 44px;
  margin-top: 4px;
  border: 0;
  border-radius: 8px;
  background: var(--ink);
  color: var(--paper);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  position: relative;
}
/* A chrome bezel around every button, rendered in 3D like the others: the ring sits 4.1px outside the edge,
   and its corners (52px of the image, 12.7px on screen) keep their shape at any width */
.im-pill::after {
  content: "";
  position: absolute;
  inset: -4.1px;
  border: 12.7px solid transparent;
  border-image: var(--im-btn-bezel) 52 / 12.7px stretch;
  pointer-events: none;
}
.im-pill:active:not(:disabled) { opacity: .8; }
.im-pill:disabled { opacity: .3; cursor: default; }
.im-pill.secondary { width: 100%; margin-top: 12px; border: 1px solid var(--im-field); background: transparent; color: var(--ink); }
.im-pill.secondary:hover:not(:disabled) { background: var(--im-soft); }
.im-error { display: flex; align-items: flex-start; justify-content: center; gap: 6px; color: var(--im-danger); font-size: 12px; line-height: 16px; text-align: left; }
.im-error:empty { display: none; }
.im-error svg { margin-top: 1px; }
.im-foot { margin-top: 24px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-link { padding: 0; border: 0; background: none; color: var(--ink); font: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line); transition: text-decoration-color .25s ease; cursor: pointer; align-self: center; }
.im-link:hover { text-decoration-color: var(--ink); }   /* underlines as faint as the site's lines, ink on hover */
.im-signin { color: var(--ink-2); }   /* returning visitors' way in on the first step: there, but quiet */
.im-card-form .im-link { font-size: 12px; }
.im-hidden { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* ---------- The app flow: the Home Screen first, notifications after joining ---------- */
.im-tile.soft { background: var(--im-soft); color: var(--ink); }
.im-install .im-tile + .im-card-title, .im-tile.soft + .im-card-title { margin-top: 24px; }
.im-steps { width: 100%; margin: 32px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--im-line); border-bottom: 1px solid var(--im-line); text-align: left; }
.im-steps li { display: flex; align-items: center; gap: 12px; padding: 12px 0; text-indent: 0; white-space: normal; }   /* not the CV's bullet lists */
.im-steps li::before { content: none; }
.im-steps li + li { border-top: 1px solid var(--im-line); }
.im-steps b { font-weight: 600; }
.im-step-ic { flex: none; width: 32px; height: 32px; display: grid; place-items: center; border-radius: 8px; background: var(--im-soft); color: var(--ink); }
.im-step-ic.ink { background: var(--ink); color: var(--paper); }
.im-eyebrow { margin: 24px 0 0; display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 11px; line-height: 16px; font-weight: 500; }
.im-install .im-eyebrow + .im-card-title, .im-join .im-eyebrow + .im-card-title { margin-top: 8px; }
.im-app-name { margin-top: 8px; font-size: 13px; line-height: 20px; font-weight: 600; }
/* The app's icon is an image (a rendered chrome C), so it keeps no tile colour of its own */
/* Chrome bezels: a ring rendered a little wider than what it circles, laid over its edge */
.im-framed { position: relative; flex: none; display: block; }
.im-framed-48 { width: 48px; height: 48px; }
.im-framed-112 { width: 112px; height: 112px; margin-bottom: 16px; }
.im-framed-112 .im-call-photo { margin-bottom: 0; }
.im-icon-btn, .im-attach, .im-send { position: relative; }
.im-bezel { position: absolute; pointer-events: none; }
.im-bezel-48 { left: -3.7px; top: -3.7px; width: 55.4px; height: 55.4px; }
.im-bezel-40 { left: -3.3px; top: -3.3px; width: 46.6px; height: 46.6px; }
.im-bezel-32 { left: -3.1px; top: -3.1px; width: 38.2px; height: 38.2px; }
.im-bezel-112 { left: -5.4px; top: -5.4px; width: 122.8px; height: 122.8px; }
.im-bezel-68 { left: -4.1px; top: -4.1px; width: 76.2px; height: 76.2px; }
.im-call-r { position: relative; }
/* Chrome step icons, on their charcoal tiles */
.im-chrome-ic { display: block; width: 40px; height: 40px; }
.im-step-ic .im-chrome-ic { width: 24px; height: 24px; }
.im-app-icon { display: block; object-fit: cover; background: #000 !important; box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent) !important; }
.im-tile.im-app-icon { width: 64px; height: 64px; border-radius: 16px; border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch; box-shadow: 0 14px 40px -12px var(--im-drop) !important; }
.im-link.im-stay { margin-top: 12px; padding: 12px 0; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-qr { margin-top: 32px; padding: 20px; border-radius: 16px; background: #000; color: #fff; border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch; box-shadow: 0 14px 40px -12px var(--im-drop); }   /* white on black in the cards' chrome, like the app icon's tile; the 20px of black round it is the code's quiet zone (phone cameras read inverted codes) */
.im-qr svg { display: block; }
.im-qr-text { margin: 12px 0 0; font-size: 12px; line-height: 16px; color: var(--im-muted); user-select: all; }
.im-pill.im-wide { width: 100%; margin-top: 32px; }
.im-notif-preview { width: 100%; box-sizing: border-box; margin-top: 32px; padding: 12px; display: flex; align-items: center; gap: 12px; border-radius: 16px; background: var(--im-soft); text-align: left; }
.im-notif-icon { flex: none; width: 36px; height: 36px; border-radius: 9px; display: grid; place-items: center; background: var(--ink); color: var(--paper); }

/* ---------- Luxury, for the visitor's onboarding ---------- */
/* Clean black at the top; metallic cream for the app's tile and the main button; lit charcoal for fields, cards and the rest */
.im-join {
  --im-metal: linear-gradient(145deg, #faf7f1 0%, #d8d2c7 42%, #f3efe8 58%, #b9b3a8 100%);
  --im-coal: linear-gradient(180deg, color-mix(in srgb, var(--ink) 11%, var(--paper)), color-mix(in srgb, var(--ink) 4%, var(--paper)));
  --im-coal-edge: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 0 0 1px color-mix(in srgb, var(--ink) 9%, transparent);
}
.im-join .im-tile, .im-join .im-step-ic.ink, .im-join .im-notif-icon {
  background: var(--im-metal);
  color: #0b0b0b;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9), inset 0 -1px 0 rgba(0, 0, 0, .18), 0 14px 40px -10px color-mix(in srgb, var(--ink) 35%, transparent);
}
.im-join .im-step-ic.ink, .im-join .im-notif-icon { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9), inset 0 -1px 0 rgba(0, 0, 0, .18); }
.im-join .im-tile.soft, .im-join .im-notif-preview { background: var(--im-coal); color: var(--ink); box-shadow: var(--im-coal-edge); }
.im-join .im-step-ic { background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 20%, var(--paper)), color-mix(in srgb, var(--ink) 10%, var(--paper))); }
.im-join .im-steps { box-sizing: border-box; padding: 0 16px; border: 0; border-radius: 16px; background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 11%, var(--paper)), color-mix(in srgb, var(--ink) 4%, var(--paper))); box-shadow: var(--im-coal-edge); }
/* The onboarding tiles and the Home Screen steps' icons sit in chrome bezels too */
.im-join .im-tile.soft { width: 64px; height: 64px; border-radius: 16px; border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch; box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 14px 40px -12px var(--im-drop); }
.im-steps .im-step-ic, .im-notif-icon.im-app-icon { border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-btn-bezel) 52 / 12.7px / 4.1px stretch; }
.im-step-ic.im-app-icon, .im-notif-icon.im-app-icon { box-shadow: none !important; }
.im-join .im-pill { background: linear-gradient(180deg, var(--ink), color-mix(in srgb, var(--ink) 80%, var(--paper))); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .55), 0 10px 28px -12px color-mix(in srgb, var(--ink) 45%, transparent); }
.im-join .im-pill.secondary { border: 0; color: var(--ink); background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 14%, var(--paper)), color-mix(in srgb, var(--ink) 6%, var(--paper))); box-shadow: var(--im-coal-edge); }
.im-join .im-field { border: 0; background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 10%, var(--paper)), color-mix(in srgb, var(--ink) 4%, var(--paper))); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 10%, transparent), 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent); }
.im-join .im-field:focus { box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 10%, transparent), 0 0 0 1px color-mix(in srgb, var(--ink) 45%, transparent); }
/* The fields and the Add to Home Screen steps carry the same chrome bezel as the buttons.
   Longhands, not the border shorthand: the shorthand would reset the border image. */
.im .im-field, .im-join .im-field { border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-btn-bezel) 52 / 12.7px / 4.1px stretch; }
.im-join .im-field { background-color: color-mix(in srgb, var(--ink) 7%, var(--paper)); }   /* under the gradient: what shows if a browser drops it */
/* Autofill keeps our field: the browser's tint never arrives (its colour change is put off indefinitely), and the
   filled text stays in our ink and Fira Code. The browser's suggestion menu, and the system-font preview Chrome shows
   while a suggestion is hovered, are the browser's own and can't be styled by any site. */
.im-field:-webkit-autofill, .im-field:-webkit-autofill:hover, .im-field:-webkit-autofill:focus, .im-field:autofill {
  -webkit-text-fill-color: var(--ink);
  caret-color: var(--ink);
  transition: background-color 100000s 0s, color 100000s 0s;
}
.im-field:-webkit-autofill::first-line { font-family: "FiraWeb", "FiraCV", monospace; font-size: 13px; }
.im-join .im-steps { border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch; }
/* The email code: eight boxes in two fours, each in the chrome bezel; the box for the next character glows */
.im-code-boxes { position: relative; display: flex; justify-content: center; gap: 10px; }   /* the rings sit 4.1px outside each box, so 10px keeps them apart */
.im-code-gap { flex: none; width: 6px; }
.im-dg { box-sizing: border-box; flex: none; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 8px; font: 600 20px/1 "FiraWeb", "FiraCV", monospace; color: var(--ink);
  background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 10%, var(--paper)), color-mix(in srgb, var(--ink) 4%, var(--paper)));
  border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-btn-bezel) 52 / 12.7px / 4.1px stretch;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 10%, transparent); }
.im-dg.on { box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 10%, transparent), 0 0 18px -4px color-mix(in srgb, var(--ink) 35%, transparent); }
.im-dg.on:empty::after { content: ""; width: 2px; height: 18px; background: var(--ink); animation: im-blink 1s steps(1) infinite; }
@keyframes im-blink { 50% { opacity: 0; } }
/* the real field: over the boxes, invisible, so a tap anywhere on them types into it */
.im-code-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; background: transparent; color: transparent; caret-color: transparent; font-size: 16px; outline: none; opacity: .01; }
.im-code-input::selection { background: transparent; }
.im-notif-text { flex: 1; min-width: 0; display: flex; flex-direction: column; color: var(--im-muted); }
.im-notif-top { display: flex; justify-content: space-between; gap: 8px; color: var(--ink); }
.im-notif-top b { font-weight: 600; }
.im-notif-top span { font-size: 12px; color: var(--im-muted); }

/* In the installed app: a header naming who you write to, in place of the site's bar */
.im { --im-head-top: 12px; }
.im-apphead { flex: none; position: relative; z-index: 4; display: flex; align-items: center; gap: 12px; padding: var(--im-head-top) var(--im-col) var(--im-gap); background: var(--paper); }
/* The card the header and its menu make shares its corners' centres with the photo's circle: the photo sits one gap in
   from the card's side and its foot, and the corner's radius is the photo's (24px) plus that gap. Phones: 8px, so 32px;
   wide screens: 14px, so 38px, which sets the card's chrome flush inside the side lines (both tubes lie 1 to 3px outside
   their own edge, so 2px in puts them side by side, touching). The chrome is re-cut for each radius */
.im { --im-gap: 8px; --im-radius: 31.9px; --im-dd-chrome: var(--im-dd-bezel-32) 144 / 0 36px 36px stretch; }
@media (min-width: 720px) { .im { --im-gap: 14px; --im-radius: 37.9px; --im-dd-chrome: var(--im-dd-bezel-38) 168 / 0 42px 42px stretch; } }   /* 14px: the card's chrome sits flush inside the side lines' */
.im-apphead::after { content: none; }   /* the header is one solid bar across the top, no fade under it */
/* A visitor's header is the More menu's card, closed: the menu's chrome edge with its rounded corners runs along its
   foot, at the menu's width. Opening the menu carries that edge down to the menu's foot; closing brings it back up */
.im-visitor .im-apphead::after { content: ""; position: absolute; top: 0; bottom: -4.1px; left: calc(var(--im-col) - var(--im-gap) - 4.1px); right: calc(var(--im-col) - var(--im-gap) - 4.1px); border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-dd-chrome); pointer-events: none; }
/* Only the card is filled: the header's own box stays clear, so beside the card and under its rounded foot the chat's
   ground shows (inside the side lines) and the black beyond them. The fill is clipped to the card's edge, 4.1px in from
   the chrome's box, its corners the card's radius */
.im-visitor .im-apphead { background: transparent; }
.im-visitor .im-apphead::after { z-index: -1; padding: 0 4.1px 4.1px; background: var(--im-grain), var(--im-ground); background-size: 180px 180px, auto; background-clip: content-box; animation: im-grain 1s steps(1) infinite; border-radius: 0 0 calc(var(--im-radius) + 4.1px) calc(var(--im-radius) + 4.1px); }

/* The More menu: a card on the plain page colour (no glow) that runs up behind the header to the window's top, 8px in from
   the edges on phones, chrome down both sides and round its foot; the header's photo, name and buttons lie on it (it is
   the header's child, under its contents). The chat behind dims and blurs */
.im-veil { position: absolute; inset: 0; z-index: 3; background: var(--im-veil); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); animation: im-fade .4s ease both; }   /* the page's own colour over the chat, so nothing turns black */
.im-dd { position: absolute; left: calc(var(--im-col) - var(--im-gap)); right: calc(var(--im-col) - var(--im-gap)); top: 0; z-index: -1; padding: calc(var(--im-head-top) + 48px + var(--im-gap)) var(--im-gap) calc(var(--im-gap) + 8px); border-radius: 0 0 var(--im-radius) var(--im-radius); text-align: left; --im-dd-rise: calc(100% - var(--im-head-top) - 48px - var(--im-gap)); }   /* rise: how far the foot sits below the header's own */
/* The card's ground and its chrome are drawn behind its contents, so the foot can travel: it starts at the header's foot
   and slides down to the menu's, the rows following */
.im-dd::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: 0 0 var(--im-radius) var(--im-radius); background: var(--im-grain), var(--im-ground); background-size: 180px 180px, auto; box-shadow: 0 30px 60px -20px var(--im-drop); }   /* the chat's moving grain over the card's colour, as on the header card */
.im-dd::before { --im-foot: 0px; }
.im-dd::after { --im-foot: -4.1px; }
.im-dd::before, .im-dd::after { animation: im-grow .5s cubic-bezier(.16, 1, .3, 1) both; }
.im-dd::before { animation: im-grow .5s cubic-bezier(.16, 1, .3, 1) both, im-grain 1s steps(1) infinite; }
@keyframes im-grow { from { bottom: calc(var(--im-dd-rise) + var(--im-foot)); } }
@keyframes im-row { from { opacity: 0; transform: translateY(-6px); } }
.im-dd-info, .im-dd-it { animation: im-row .45s cubic-bezier(.16, 1, .3, 1) backwards; }   /* backwards: once in, the rows are free to answer the pointer */
.im-dd-info { animation-delay: .08s; }
.im-dd-it:nth-child(1) { animation-delay: .14s; }
.im-dd-it:nth-child(2) { animation-delay: .19s; }
.im-dd-it:nth-child(3) { animation-delay: .24s; }
.im-dd-it:nth-child(4) { animation-delay: .29s; }
/* Closing runs the opening backwards: the rows fade first, the foot slides back up into the header, the veil clears */
@keyframes im-shrink { to { bottom: calc(var(--im-dd-rise) + var(--im-foot)); } }
@keyframes im-row-out { to { opacity: 0; transform: translateY(-6px); } }
@keyframes im-clear { to { opacity: 0; } }
.im-dd.im-out { pointer-events: none; }
.im-dd.im-out::before, .im-dd.im-out::after { animation: im-shrink .32s cubic-bezier(.4, 0, .2, 1) .06s both; }
.im-dd.im-out::before { animation: im-shrink .32s cubic-bezier(.4, 0, .2, 1) .06s both, im-grain 1s steps(1) infinite; }
.im-dd.im-out .im-dd-info, .im-dd.im-out .im-dd-it { animation: im-row-out .22s cubic-bezier(.4, 0, 1, 1) both; animation-delay: 0s; }
.im-veil.im-out { animation: im-clear .38s ease both; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .im-dd::before, .im-dd::after, .im-dd.im-out::before, .im-dd.im-out::after, .im-dd-info, .im-dd-it, .im-veil, .im-dd.im-out, .im-veil.im-out { animation: none; } }
/* The chrome bezel down its sides and along its rounded foot */
.im-dd::after { content: ""; position: absolute; inset: 0 -4.1px -4.1px; border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-dd-chrome); clip-path: inset(-1px 0 -1px); pointer-events: none; }
.im-apphead.open::after { display: none; }
/* On wider screens it hangs at the width of the conversation, not the window: the column plus the gap (14px) either side, inside the side lines */
/* Inside, everything keeps the header's grid: icons centred in the photo's column (48px), words on the name's line (12px
   past it), dividers starting from the words and ending under the buttons' edge */
.im-dd-info { padding: 12px 0 16px 60px; }
.im-dd-t { position: relative; display: block; font-weight: 600; }
.im-dd-t svg { position: absolute; left: -43px; top: 3px; width: 14px; height: 14px; color: var(--im-muted); }
.im-dd-info p { margin: 4px 0 0; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-dd-it { position: relative; width: 100%; height: 48px; display: flex; align-items: center; padding: 0; border: 0; background: none; color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.im-dd-it::before { content: ""; position: absolute; top: 0; left: 60px; right: 0; border-top: 1px solid var(--im-line); }
.im-dd-it svg { flex: none; width: 18px; height: 18px; margin: 0 27px 0 15px; color: var(--im-muted); }
.im-dd-it.red, .im-dd-it.red svg { color: #f2645c; }
.im-dd-it:disabled { opacity: .5; cursor: default; }
/* Rows answer the pointer as the site's own menu does: the row under it stays bright and the others fade to 35%; its icon
   leans 2px the way the row goes (Back to Website's arrow leans back); pressing dims the row and sets the icon down a touch */
.im-dd-it > * { transition: opacity .25s ease; }
.im-dd-it svg { transition: transform .25s cubic-bezier(.16, 1, .3, 1), opacity .25s ease; }
@media (hover: hover) {
  .im-dd-rows:hover .im-dd-it:not(:hover):not(:disabled) > * { opacity: .35; }   /* the words and icons fade, the dividers stay */
  .im-dd-it:not(:disabled):hover svg { transform: translateX(2px); }
  .im-dd-it.back:not(:disabled):hover svg { transform: translateX(-2px); }
}
.im-dd-it:not(:disabled):active > * { opacity: .6; transition-duration: .08s; }
.im-dd-it:not(:disabled):active svg { transform: scale(.9); transition-duration: .08s; }
@media (prefers-reduced-motion: reduce) { .im-dd-it svg { transition: none; transform: none !important; } }
html.standalone .im { --im-head-top: max(4px, env(safe-area-inset-top)); }   /* in the app the photo sits right under the status bar */
.im-apphead-photo { display: block; width: 48px; height: 48px; border-radius: 50%; object-fit: cover; background: var(--im-avatar); box-shadow: 0 8px 20px -10px var(--im-drop); }
.im-apphead-who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.im-apphead-name { font-weight: 600; }
.im-apphead-sub { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-apphead-sub svg { width: 10px; height: 10px; }
.im-apphead-tools { flex: none; display: flex; gap: 8px; padding: 0; }
.im-icon-btn:disabled { opacity: .4; cursor: default; }
.im-thread-bar .im-call-btn { margin-left: auto; }

/* ---------- A voice call ---------- */
/* It covers the chat: photo, name, how it is going, and two round buttons at the foot */
.im-call {
  position: fixed;
  inset: 0;
  z-index: 50;
  font: 13px/20px "FiraWeb", "FiraCV", monospace;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: calc(72px + env(safe-area-inset-top)) 24px calc(48px + env(safe-area-inset-bottom));
  text-align: center;
  color: var(--ink);
  background: radial-gradient(150% 42% at 50% 104%, color-mix(in srgb, var(--ink) 15%, transparent), transparent 72%), var(--paper);
  animation: im-fade .25s ease both;
}
.im-call-who { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.im-call-photo, .im-call-av { width: 112px; height: 112px; margin-bottom: 16px; border-radius: 50%; }
.im-call-photo { display: block; object-fit: cover; background: var(--im-avatar); box-shadow: 0 16px 40px -14px var(--im-drop); }
.im-call-av { display: grid; place-items: center; font-size: 36px; font-weight: 600; background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 18%, var(--paper)), color-mix(in srgb, var(--ink) 8%, var(--paper))); }
.im-call-name { font-size: 20px; line-height: 28px; font-weight: 600; }
.im-call-st { font-size: 16px; line-height: 24px; color: var(--im-muted); font-variant-numeric: tabular-nums; }
.im-call-e2e { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-call-e2e svg { width: 10px; height: 10px; }
.im-call-ctl { margin-top: auto; width: min(320px, 100%); display: flex; justify-content: space-around; }
.im-call-b { display: flex; flex-direction: column; align-items: center; gap: 8px; font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-call-r {
  width: 68px;
  height: 68px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: var(--ink);
  background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 18%, var(--paper)), color-mix(in srgb, var(--ink) 9%, var(--paper)));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent);
}
/* On a call the knobs' lights say what they do, lit all the time (phones have no hover): End and Decline red, Accept green,
   Mute white and pressed in while it is on. Sharp, like the hover light, with the hard rim on light metal */
.im-call-r.im-knob.end, [data-theme="dark"] .im-call-r.im-knob.end { color: #ff4a3d; }
.im-call-r.im-knob.accept, [data-theme="dark"] .im-call-r.im-knob.accept { color: #3ee06a; }
.im-call-r.im-knob.on, [data-theme="dark"] .im-call-r.im-knob.on { color: #fff; filter: brightness(.82); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .55), 0 0 0 rgba(0, 0, 0, 0); }
:root:not([data-theme="dark"]) .im-call-r.im-knob:is(.end, .accept, .on) svg { filter: drop-shadow(0 -.5px 0 rgba(0, 0, 0, .55)) drop-shadow(0 .5px 0 rgba(0, 0, 0, .3)) drop-shadow(.5px 0 0 rgba(0, 0, 0, .25)) drop-shadow(-.5px 0 0 rgba(0, 0, 0, .25)); }
.im-call-r:disabled { opacity: .5; cursor: default; }

/* ---------- Dialogs ---------- */
.im-shade {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: color-mix(in srgb, var(--paper) 55%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.im-shade.im-sheet { align-items: flex-end; padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
/* Dialogs are chrome cards like the app icon's tile: 16px corners in the cards' chrome, the page colour inside */
.im-alert, .im-actions {
  width: min(360px, 100%);
  padding: 24px 24px 16px;
  border-style: solid;
  border-width: 0;
  border-color: transparent;
  border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch;
  border-radius: 16px;
  background: var(--paper);
  box-shadow: 0 30px 60px -20px var(--im-drop);
  text-align: center;
}
.im-alert h2 { margin: 0; padding: 0; font-size: 16px; line-height: 24px; font-weight: 600; letter-spacing: 0; text-transform: none; }   /* undo the CV's section h2 */
.im-alert p { margin-top: 4px; color: var(--im-muted); font-size: 12px; line-height: 16px; }
.im-alert input { width: 100%; height: 44px; margin: 16px 0 12px; padding: 0 12px; border: 1px solid var(--im-field); border-radius: 8px; background: var(--paper); color: var(--ink); font: 13px "FiraWeb", "FiraCV", monospace; text-align: center; outline: none; }
.im-alert .im-pill { width: 100%; }
.im-actions { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
/* Its choices are 48px rows across the card's full width under hairlines, as in the More menu: the destructive one
   in the menu's red, Cancel quiet. The row under the pointer stays bright and the others' words fade */
.im-actions button { height: 48px; flex: none; border: 0; border-top: 1px solid var(--im-line); border-radius: 0; background: none; color: var(--ink); font: inherit; font-size: 13px; cursor: pointer; transition: color .25s ease; }
.im-actions button:disabled { color: var(--im-muted); cursor: default; }
.im-actions button.danger { color: #f2645c; }
.im-actions button.im-cancel { color: var(--im-muted); }
@media (hover: hover) {
  .im-actions:hover button:not(:hover):not(:disabled) { color: color-mix(in srgb, var(--ink) 35%, transparent); }   /* the words fade, the hairlines stay */
  .im-actions:hover button.danger:not(:hover) { color: color-mix(in srgb, #f2645c 35%, transparent); }
  .im-actions:hover button.im-cancel:not(:hover) { color: color-mix(in srgb, var(--im-muted) 35%, transparent); }
  .im-actions button.im-cancel:hover { color: var(--ink); }
}
.im-actions button:not(:disabled):active { color: color-mix(in srgb, var(--ink) 60%, transparent); transition-duration: .08s; }
.im-sheet-head { padding: 24px 24px 20px; display: flex; flex-direction: column; gap: 8px; }
.im-sheet-title { font-size: 16px; line-height: 24px; font-weight: 600; }
.im-sheet-title svg { display: inline-block; margin-right: 8px; vertical-align: -1px; }
.im-sheet-head p { font-size: 12px; line-height: 16px; color: var(--im-muted); }
.im-sheet-head .im-sheet-who { margin-top: 0; color: var(--ink); overflow-wrap: anywhere; }
@media (min-width: 641px) { .im-shade.im-sheet { align-items: center; } }   /* a dialog on wide screens, a sheet on phones */

.im-thread.drop .im-log { outline: 1.5px dashed var(--ink); outline-offset: -10px; border-radius: 12px; }

/* Inputs keep the body size on phones; cv.html stops iOS zooming into them instead */

/* ---------- Motion ---------- */
@media (prefers-reduced-motion: no-preference) {
  @keyframes im-pop { from { transform: scale(.8); } }
  @keyframes im-rise { from { opacity: 0; transform: translateY(8px); } }
  @keyframes im-fade { from { opacity: 0; } }
  .im-bubble.new { animation: im-pop .32s cubic-bezier(.34, 1.56, .64, 1) both; }   /* a new bubble grows out of its bottom corner */
  .im-bubble.mine.new { transform-origin: 100% 100%; }
  .im-bubble.theirs.new { transform-origin: 0 100%; }
  .im-enter .im-alert, .im-enter .im-actions { animation: im-rise .6s cubic-bezier(.16, 1, .3, 1) both; }
  /* A screen arriving (install, join, code, passkey, notifications, sign-in) assembles itself: the icon tile settles in
     first (from a little smaller, soft focus), then each piece in reading order rises 12px out of a soft blur, 80ms
     apart: the label, the title, the line, the QR or steps, each field, then the button and the link under it.
     Only when the screen changes; a repaint of the same screen (an error, a busy button) never replays it */
  @keyframes im-in { from { opacity: 0; transform: translateY(12px); filter: blur(4px); } }
  @keyframes im-tile-in { from { opacity: 0; transform: scale(.88); filter: blur(6px); } }
  .im-card.im-enter > *, .im-card.im-enter .im-card-form > *, .im-card.im-enter .im-step-acts > * { animation: im-in .9s cubic-bezier(.16, 1, .3, 1) both; }
  .im-card.im-enter > .im-tile:first-child, .im-card.im-enter > .im-app-icon:first-child { animation-name: im-tile-in; animation-duration: 1s; }
  .im-card.im-enter > .im-card-form, .im-card.im-enter .im-card-form > .im-step-acts { animation: none; }   /* their pieces arrive one by one instead */
  .im-card.im-enter > :nth-child(2) { animation-delay: .1s; }
  .im-card.im-enter > :nth-child(3) { animation-delay: .18s; }
  .im-card.im-enter > :nth-child(4) { animation-delay: .26s; }
  .im-card.im-enter > :nth-child(5) { animation-delay: .34s; }
  .im-card.im-enter > :nth-child(6) { animation-delay: .42s; }
  .im-card.im-enter > :nth-child(7) { animation-delay: .5s; }
  .im-card.im-enter > :nth-child(8) { animation-delay: .58s; }
  .im-card.im-enter > :nth-child(9) { animation-delay: .66s; }
  .im-card.im-enter .im-card-form > :nth-child(1) { animation-delay: .36s; }
  .im-card.im-enter .im-card-form > :nth-child(2) { animation-delay: .44s; }
  .im-card.im-enter .im-card-form > :nth-child(3) { animation-delay: .52s; }
  .im-card.im-enter .im-step-acts > :nth-child(1) { animation-delay: .52s; }
  .im-card.im-enter .im-step-acts > :nth-child(2) { animation-delay: .6s; }
  .im-card.im-enter .im-step-acts > :nth-child(3) { animation-delay: .7s; }
  /* the site's bar and footer come up softly behind the first screen, once */
  body.im-flow .site-nav .bar, body.im-flow .site-foot > * { animation: im-fade 1.2s .2s ease both; }
  /* an error arriving on a screen already shown: only the error moves, easing in from the right inside its field, or in place above the button */
  .im-field-err, .im-step-acts > .im-error:not(:empty) { animation: im-err-in .35s cubic-bezier(.16, 1, .3, 1) both; }
  @keyframes im-err-in { from { opacity: 0; transform: translateX(6px); } }
  .im-shade.im-enter, .im-picker { animation: im-fade .2s ease both; }
  .im-acts { transition: opacity .15s ease; }
  .im-input, .im-send, .im-row, .im-back, .im-act, .im-pillbox, .im-field {
    transition: border-color .2s ease, background-color .2s ease, color .2s ease, box-shadow .2s ease;
  }
}
.im-pillbox .im-input:focus-visible { outline: none; }   /* the pill's own border shows focus */

/* A picture, full screen: tapped in the conversation it opens over everything on the page's own colour, blurred, with a
   raised X in chrome at the top right (Escape, or a tap around the picture, also closes it). It sits outside .im, so it
   uses the site's own tokens */
html.im-viewing { overflow: hidden; }
.im-viewer { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; box-sizing: border-box; padding: calc(68px + env(safe-area-inset-top)) 16px calc(32px + env(safe-area-inset-bottom)); background: color-mix(in srgb, var(--paper) 86%, transparent); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); animation: im-viewer-in .3s ease both; }
.im-viewer img { display: block; width: auto; height: auto; max-width: min(100%, 1080px); max-height: calc(100dvh - 100px - env(safe-area-inset-top) - env(safe-area-inset-bottom)); border-radius: 16px; object-fit: contain; box-shadow: 0 30px 80px -30px #000; animation: im-viewer-img .4s cubic-bezier(.16, 1, .3, 1) both; }
.im-viewer-x { position: absolute; top: calc(12px + env(safe-area-inset-top)); right: max(16px, calc((100% - 680px) / 2)); width: 40px; height: 40px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 50%; color: var(--ink); cursor: pointer;
  background: linear-gradient(180deg, color-mix(in srgb, var(--ink) 18%, var(--paper)), color-mix(in srgb, var(--ink) 9%, var(--paper)));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink) 12%, transparent), 0 8px 20px -10px #000; }
.im-viewer-x .im-bezel { position: absolute; pointer-events: none; }
.im-viewer.im-out { animation: im-viewer-out .26s ease both; pointer-events: none; }
.im-viewer.im-out img { animation: im-viewer-img-out .26s ease both; }
@keyframes im-viewer-in { from { opacity: 0; } }
@keyframes im-viewer-out { to { opacity: 0; } }
@keyframes im-viewer-img { from { opacity: 0; transform: scale(.96); } }
@keyframes im-viewer-img-out { to { opacity: 0; transform: scale(.97); } }
@media (prefers-reduced-motion: reduce) { .im-viewer, .im-viewer img { animation: none; } }

/* Gunmetal, in dark mode, recessed: the inside of the message bar, the join and sign-in fields and the GIF search are cut
   into the surface (debossed). Near black steel with the switch's fine grain, darkest under the top lip where its shadow
   falls and lifting toward the floor; a soft inner shadow along the top and a fine catch-light on the bottom lip, where the
   light meets the far edge. Their chrome rims stay as they are */
[data-theme="dark"] .im {
  --im-gunmetal: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/></filter><rect width='80' height='80' filter='url(%23g)'/></svg>"), linear-gradient(180deg, #050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%);
  --im-recess: inset 0 3px 6px rgba(0, 0, 0, .85), inset 0 1px 1px rgba(0, 0, 0, .7), inset 0 -1px 0 rgba(255, 255, 255, .07);
}
[data-theme="dark"] .im-dock .im-compose,
[data-theme="dark"] .im .im-field,
[data-theme="dark"] .im-join .im-field,
[data-theme="dark"] .im-gifs-search { background: var(--im-gunmetal); background-color: #0c0d0e; box-shadow: var(--im-recess); }
[data-theme="dark"] .im-dock .im-compose { box-shadow: var(--im-recess), 0 16px 40px -16px var(--im-drop); }
/* in use, a faint light ring inside the recess */
[data-theme="dark"] .im-dock .im-compose:focus-within { box-shadow: var(--im-recess), inset 0 0 0 1px rgba(255, 255, 255, .08), 0 16px 40px -16px var(--im-drop); }
[data-theme="dark"] .im .im-field:focus,
[data-theme="dark"] .im-join .im-field:focus,
[data-theme="dark"] .im-gifs-search:focus-within { box-shadow: var(--im-recess), inset 0 0 0 1px rgba(255, 255, 255, .14); }

/* ---------- Every button machined ----------
   The long buttons are the message bar's gunmetal raised: near-black steel with its fine grain (#18191b under a faint
   top light to #08080a at the foot, no lines on the face) in their chrome bezel, the label cut into it and filled from
   behind: unlit, dim steel darker under its top edge; lit, light itself (a hot white core at the label's middle,
   cooling to a pale steel-blue at its ends and edges), clipped to the letters so none spills on the face.
   Under the pointer the engraving lights from inside, white and sharp, like the knobs; the face stays as it is. The send
   knob's arrow is lit while there is something to send. Disabled buttons sit dim and never light */
.im .im-pill, .im .im-pill.secondary, .im-join .im-pill, .im-join .im-pill.secondary {
  border: 0;
  background:
    var(--im-grain),
    linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, 0) 50%),
    linear-gradient(180deg, #18191b, #0e0f10 55%, #080809);
  background-size: 180px 180px, auto, auto;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), 0 1px 3px rgba(0, 0, 0, .6), 0 10px 28px -12px rgba(0, 0, 0, .9);
  text-shadow: none;
  transition: transform .15s ease, filter .25s ease;
}
/* The label (.im-lbl, wrapped round each long button's words after a paint) is a cut in the metal filled from behind:
   unlit, dim steel darker under its top edge; lit, the light itself */
.im .im-pill .im-lbl {
  color: transparent; -webkit-text-fill-color: transparent;
  background: linear-gradient(180deg, #3b3d41, #55575b 55%, #696b6f);
  -webkit-background-clip: text; background-clip: text;
}
.im .im-pill:active:not(:disabled), .im-join .im-pill:active:not(:disabled), .im-join .im-pill.secondary:active:not(:disabled) { opacity: 1; transform: scale(.985); }
.im .im-pill:active:not(:disabled) .im-lbl { background: radial-gradient(ellipse 42% 80% at 50% 52%, #ffffff, rgba(255, 255, 255, .6) 45%, rgba(255, 255, 255, 0) 100%), linear-gradient(180deg, #9aa5b6, #dde3ec 36%, #ffffff 50%, #d2d9e4 66%, #8f9aab); -webkit-background-clip: text; background-clip: text; }   /* a press lights it too, so phones (no hover) see the light */
.im-dock .im-send, .im-dock .im-send.on, .im .im-pillbox .im-send.on {
  color: #fff;
  background: radial-gradient(circle at 50% 30%, rgba(255, 255, 255, .18), rgba(255, 255, 255, 0) 60%), conic-gradient(from 20deg, #5d5f63, #2c2d30, #4e5054, #232426, #5d5f63, #35363a, #56585c, #28292c, #5d5f63);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .6);
}
.im-dock .im-send svg { filter: drop-shadow(0 .6px 0 rgba(255, 255, 255, .28)) drop-shadow(0 -.5px 0 rgba(0, 0, 0, .55)); }
.im-dock .im-send:active { transform: scale(.94); }
@media (hover: hover) {
  .im .im-pill:hover:not(:disabled), .im-join .im-pill.secondary:hover:not(:disabled) { background: var(--im-grain), linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, 0) 50%), linear-gradient(180deg, #18191b, #0e0f10 55%, #080809); background-size: 180px 180px, auto, auto; }
  .im .im-pill:hover:not(:disabled) .im-lbl { background: radial-gradient(ellipse 42% 80% at 50% 52%, #ffffff, rgba(255, 255, 255, .6) 45%, rgba(255, 255, 255, 0) 100%), linear-gradient(180deg, #9aa5b6, #dde3ec 36%, #ffffff 50%, #d2d9e4 66%, #8f9aab); -webkit-background-clip: text; background-clip: text; }
}

/* ---------- The cipher's name, explained ----------
   AES-256-GCM in the joining line is a term with the site's faint underline. A click opens it: the window dims as it
   does behind the More menu (the same veil, darkening and blur) and a small chrome card with the grain comes down over
   the line itself. Both lie in one fixed layer over the page (.im-term-layer); any click closes it */
.im-term { padding: 0; border: 0; background: none; color: var(--ink); font: inherit; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line); transition: text-decoration-color .25s ease; }
.im-term:hover, .im-term[aria-expanded="true"], .im-term:focus-visible { text-decoration-color: var(--ink); outline: none; }
.im.im-term-layer { position: fixed; inset: 0; z-index: 100; height: auto; background: none; pointer-events: none; }
.im-term-layer .im-veil.im-term-veil { position: absolute; inset: 0; z-index: 0; background: rgba(0, 0, 0, .72); }   /* the menu's veil, over the whole window */
.im-term-card {
  position: absolute; z-index: 2; pointer-events: auto;
  padding: 16px; border-radius: 16px; text-align: left; font-size: 12px; line-height: 18px; color: var(--ink-2); cursor: pointer;
  background: var(--im-grain), var(--im-ground); background-size: 180px 180px, auto;
  border-style: solid; border-width: 0; border-color: transparent; border-image: var(--im-card-bezel) 81 / 20.1px / 4.1px stretch;
  box-shadow: 0 24px 50px -16px #000;
  animation: im-term-in .25s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes im-term-in { from { opacity: 0; transform: translateY(-4px); } }
.im-term-layer.im-out { animation: im-clear .28s ease both; }
@media (prefers-reduced-motion: reduce) { .im-term-card, .im-term-layer.im-out, .im-term-layer .im-veil { animation: none; } }
.im-term-card p { margin: 0; }

/* A field's own error sits inside it at the right (red, with the warning mark), the field's edge warmed red; the words
   typed stop short of it. Other errors have a line above the button whose space is always kept, so nothing moves */
.im-field-box { position: relative; width: 100%; }
.im-field-err { position: absolute; top: 0; bottom: 0; right: 14px; display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 16px; color: var(--im-danger); pointer-events: none; }
.im-field-err svg { flex: none; }
.im-field-box.bad .im-field { padding-right: 168px; }
.im .im-field-box.bad .im-field, [data-theme="dark"] .im .im-field-box.bad .im-field { box-shadow: var(--im-recess, 0 0 0 0 transparent), inset 0 0 0 1px color-mix(in srgb, var(--im-danger) 55%, transparent); }
.im-step-acts > .im-error:empty { display: block; visibility: hidden; height: 16px; }

/* ---------------- The phone's foot and its keyboard ---------------- */
@media screen {
  /* The installed app on iPhone: WebKit sizes a fixed box (top 0, bottom 0) to a viewport that stops short of the screen
     by about the home indicator's inset (WebKit bug 254868), which left a dark band under the composer. The shell takes
     the screen's whole height instead (100vh is the whole screen there), on the thread's own ground */
  html.standalone #view-message:not([hidden]) { bottom: auto !important; height: 100vh !important; }
  html.chat.standalone, html.chat.standalone body { background-color: var(--im-ground); overscroll-behavior: none; }
  /* Phones: the composer sits just above the home indicator, the safe area alone (or, where WebKit's viewport stops
     short of the screen, that shortfall, measured in messages.js as --im-short, so the field stays where touches land) */
  @media (hover: none) {
    .im-dock { padding-bottom: max(8px, env(safe-area-inset-bottom), var(--im-short, 0px)); }
  }
  /* The keyboard up (html.im-kb, from trackKeyboard() in messages.js): the shell is the part of the screen above it, so
     the header stays at the top and the log and composer shrink to sit on the keyboard, the latest message in view */
  /* (the id twice: the signed-in and app rules above pin the shell to top 0 with !important, which beat this one before,
     so with iOS panning the page the header went off the top of the screen) */
  html.im-kb #view-message#view-message:not([hidden]) { top: var(--im-vv-top, 0px) !important; bottom: auto !important; height: var(--im-vv-h, 100%) !important; }
  html.im-kb .im-dock { padding-bottom: 8px; }
  html.im-kb .im-log.im-join { padding-bottom: 16px; }
  html.im-kb, html.im-kb body { overflow: hidden; overscroll-behavior: none; }
  /* While the keyboard slides, the grain holds still, so the whole thread isn't repainted under the animation */
  html.im-kb-move body.im-in .im-visitor .im-thread::before, html.im-kb-move .im-visitor .im-apphead::after { animation-play-state: paused; }
  /* Taps answer at once: no double-tap zoom to wait for */
  .im { touch-action: manipulation; }
}

/* ---------------- The visitor's header: a capsule floating under the status bar ---------------- */
/* The header is a pill all the way round (the same chrome on all four sides), the same distance in from the screen's
   sides as from the status bar (in the app the safe area clears the clock and the Dynamic Island). It floats over the
   thread: the conversation runs up behind it and behind the status bar, fading out under the clock and battery so they
   stay readable. The More menu opens out of the capsule, the same card carried down, rounded at all four corners */
.im {
  --im-cap-top: max(var(--im-gap), env(safe-area-inset-top));
  --im-cap-chrome: var(--im-dd-bezel-32) 144 / 36px stretch;
  --im-cap-h: calc(48px + 2 * var(--im-gap));
}
@media (min-width: 720px) { .im { --im-cap-chrome: var(--im-dd-bezel-38) 168 / 42px stretch; } }
.im-visitor .im-apphead.im-cap {
  position: absolute; top: 0; left: 0; right: 0;
  padding: calc(var(--im-cap-top) + var(--im-gap)) var(--im-col) var(--im-gap);
  /* the fade behind the status bar: the thread's ground at the very top, clear by the capsule's middle */
  background: linear-gradient(to bottom, rgba(14, 14, 15, .97) 0, rgba(14, 14, 15, .94) calc(var(--im-cap-top) - 8px), rgba(14, 14, 15, 0) calc(var(--im-cap-top) + var(--im-cap-h) / 2));
}
.im-visitor .im-apphead.im-cap::after {
  top: calc(var(--im-cap-top) - 4.1px); bottom: -4.1px;
  padding: 4.1px; border-image: var(--im-cap-chrome);
  border-radius: calc(var(--im-radius) + 4.1px);
  box-shadow: 0 14px 30px -18px rgba(0, 0, 0, .9);
}
/* The thread starts at the screen's top, its first message just under the capsule */
.im-visitor .im-apphead.im-cap ~ .im-log { padding-top: calc(var(--im-cap-top) + var(--im-cap-h) + 12px); }
/* Open: the closed capsule stays where it is, under the menu's card, so nothing blinks as one hands over to the other */
.im-visitor .im-apphead.im-cap.open::after { display: block; z-index: -2; }
.im-cap .im-dd {
  top: var(--im-cap-top);
  padding-top: var(--im-cap-h);
  border-radius: var(--im-radius);
  --im-dd-rise: calc(100% - var(--im-cap-h));
}
.im-cap .im-dd::before { border-radius: var(--im-radius); }
.im-cap .im-dd::after { inset: -4.1px; border-image: var(--im-cap-chrome); clip-path: none; }

/* ---------------- The More menu, always drawn ---------------- */
/* The menu and its veil are part of the screen from the start, on layers of their own, hidden by opacity alone (the
   menu inert while closed). Opening only turns them up: no new boxes to draw, no picture to decode, no blur to work
   out on the first frame, which on iPhone had left the chrome showing a beat after the tap. Only opacity and transform
   move, in 160ms, with nothing held back */
.im-cap .im-dd { opacity: 0; pointer-events: none; will-change: opacity; transform: translateZ(0); transition: opacity .16s cubic-bezier(.2, 0, 0, 1); }
.im-cap.open .im-dd { opacity: 1; pointer-events: auto; }
.im-cap .im-dd::after { animation: none; }
.im-cap .im-dd::before { animation: im-grain 1s steps(1) infinite; animation-play-state: paused; }
.im-cap.open .im-dd::before { animation-play-state: running; }
.im-cap .im-dd-info, .im-cap .im-dd-it { animation: none; }
.im-cap .im-dd-info, .im-cap .im-dd-rows { transform: translateY(-4px); transition: transform .18s cubic-bezier(.2, 0, 0, 1); }
.im-cap.open .im-dd-info, .im-cap.open .im-dd-rows { transform: none; }
/* The veil: a solid dim, no blur (a backdrop blur is worked out afresh on the frame it appears, the slowest thing to show) */
.im-thread > .im-veil { opacity: 0; pointer-events: none; animation: none; -webkit-backdrop-filter: none; backdrop-filter: none; will-change: opacity; transform: translateZ(0); transition: opacity .16s ease; }
body.im-in .im-thread > .im-veil { background: rgba(0, 0, 0, .8); }
.im-thread > .im-veil.on { opacity: 1; pointer-events: auto; }
/* Wide screens: the side lines dim with the veil, on its pace */
@media (min-width: 720px) {
  body.im-in .im-visitor .im-apphead.im-cap::before { transition: opacity .16s ease; }
  body.im-in .im-visitor .im-apphead.im-cap.open::before { animation: none; transition: opacity .16s ease; }
}
@media (prefers-reduced-motion: reduce) {
  .im-cap .im-dd, .im-cap .im-dd-info, .im-cap .im-dd-rows, .im-thread > .im-veil { transition: none; }
}
.im-apphead-tools .im-knob > * { pointer-events: none; }   /* the button itself is what a touch lands on, so the icon swapped under the finger never loses the tap (or its tick) */
