/* ==========================================================================
   Tab Truck — web app (PWA)
   Same system as the extension: 2px ink outlines, flat sticker shadows.
   Class names match what app.js emits (.group/.note/.share-row/.status…),
   so only the paint changes here.
   ========================================================================== */

* { box-sizing: border-box; }

/* ==========================================================================
   Header
   ========================================================================== */
header {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px;
  background: var(--white);
  border-bottom: var(--outline);
}
header h1 { font-size: 19px; margin: 0; flex: 1; }

.wrap { max-width: 720px; margin: 0 auto; padding: var(--s3) var(--s3) var(--s6); }

/* ==========================================================================
   Buttons
   ========================================================================== */
button {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
  background: var(--white);
  border: var(--outline);
  border-radius: 12px;
  padding: 10px 16px;
  box-shadow: var(--lift-sm);
  cursor: pointer;
  min-height: 44px;                 /* hit target — this is the mobile surface */
  white-space: nowrap;              /* keep labels like "Save link" on one line */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  transition: transform var(--t-fast) var(--ease-fast), box-shadow var(--t-fast) var(--ease-fast), background var(--t-fast) var(--ease-fast);
}
button:hover { background: var(--panel); transform: translateY(-1px); box-shadow: var(--lift); }
button:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--shadow-neutral); }

button.primary { background: var(--orange); color: #fff; box-shadow: var(--lift-orange); }
button.primary:hover { background: var(--orange-hover); transform: translateY(-1px); box-shadow: var(--lift-hover-orange); }
button.primary:active { background: var(--orange-press-bg); transform: translateY(2px); box-shadow: 0 1px 0 var(--orange-press); }

button:disabled {
  background: var(--panel); color: var(--ink-muted);
  border-color: var(--shadow-neutral); box-shadow: none;
  cursor: not-allowed; transform: none;
}

.mini {
  padding: 6px 11px; font-size: 12px; border-radius: 9px;
  min-height: 0; box-shadow: 0 2px 0 var(--shadow-neutral);
}
.mini:active { transform: translateY(2px); box-shadow: 0 0 0 var(--shadow-neutral); }
.mini.danger { color: var(--orange-link); }
.mini.danger:hover { background: var(--orange-100); }

/* ==========================================================================
   Inputs
   ========================================================================== */
input[type=text],
input[type=url],
input[type=password],
textarea,
select {
  width: 100%;
  font-family: var(--font-body);
  font-size: 15px;                  /* ≥16px avoids iOS zoom-on-focus; 15 is the compromise the rest of the UI uses */
  color: var(--ink);
  background: var(--white);
  border: var(--outline);
  border-radius: 11px;
  padding: 11px 13px;

}
input::placeholder, textarea::placeholder { color: var(--ink-muted); }
input:focus, textarea:focus, select:focus { border-color: var(--green); }   /* focus = green ring */

/* Only vertical: a horizontally-resizable textarea can be dragged wider than the
   card that holds it, and the sticker outline then runs off the edge. */
textarea { resize: vertical; min-height: 84px; line-height: 1.5; }

select {
  font-family: var(--font-display); font-weight: 600;
  cursor: pointer;
  appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 10 10'%3E%3Cpath d='M2 4l3 3 3-3' fill='none' stroke='%232A2620' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
}

/* wrap, don't overflow: on a narrow phone a button drops to its own line rather
   than spilling out of the card */
.row { display: flex; flex-wrap: wrap; gap: var(--s1); margin: 10px 0; }
.hidden { display: none !important; }
.muted { color: var(--ink-soft); font-size: 13px; }

/* ==========================================================================
   Cards
   ========================================================================== */
.card {
  background: var(--white);
  border: var(--outline);
  border-radius: var(--r-card);
  box-shadow: var(--lift);
  padding: 18px;
  margin: var(--s2) 0;
}
.card h2 { font-size: 17px; margin: 0 0 6px; }

/* The carried draft, shown above the pitch when someone arrives mid-send. Tinted and
   outlined so it reads as "here is your thing, safe" rather than as another paragraph
   of marketing. */
.signin-draft {
  margin: 0 0 var(--s3);
  padding: 13px 15px;
  background: var(--green-100);
  border: var(--outline);
  border-radius: 12px;
}
.signin-draft b { font-family: var(--font-display); font-size: 15px; }
.signin-draft blockquote {
  margin: 8px 0;
  padding-left: 11px;
  border-left: 3px solid var(--green);
  font-size: 14px; line-height: 1.5;
  /* The draft is the user's own text off a textarea: keep their line breaks, and break
     an unbroken paste (a long URL — the common case) instead of widening the card. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.signin-draft-files {
  list-style: none; margin: 8px 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px;
}
.signin-draft-files li {
  font-size: 13px; font-weight: 600;
  background: var(--white); border: var(--hair); border-radius: 9px;
  padding: 6px 10px; word-break: break-all;
}
.signin-draft-files li::before { content: "📎 "; }
.signin-draft p { margin: 0; font-size: 13px; line-height: 1.45; color: var(--ink-soft); }

/* Sign-in benefits — why connecting is worth it, before the button. */
.benefits { list-style: none; margin: var(--s2) 0 var(--s3); padding: 0; display: grid; gap: 10px; }
.benefits li { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; line-height: 1.45; }
/* The icon sizes off its own font-size (.ico is 1.05em), so this is what makes
   the glyph bigger than the 13px copy it sits beside. */
.benefits li > [data-icon] { font-size: 18px; line-height: 1.2; flex: none; color: var(--ink-soft); }

/* ==========================================================================
   Status line
   ========================================================================== */
/* Sticky under the header, so a message written by a button two screens down is still
   read. Empty most of the time, so it collapses to nothing and the tinted pill only
   appears when there is something to say — a permanently reserved bar at the top of a
   long panel is its own kind of noise. */
.status {
  position: sticky; top: 56px; z-index: 9;
  min-height: 0; margin: 10px 0;
  color: var(--ink-soft); font-size: 13px; font-weight: 600;
}
.status:not(:empty) {
  padding: 9px 12px;
  border-radius: 11px;
  border: var(--outline);
  background: var(--white);
  box-shadow: var(--lift-sm);
}
.status.err:not(:empty)  { background: var(--orange-100); }
.status.warn:not(:empty) { background: var(--orange-100); }
.status.ok:not(:empty)   { background: var(--green-100); }
.status.err  { color: var(--orange-press); }
.status.warn { color: var(--orange-link); }
.status.ok   { color: var(--green-press); }

/* First-run welcome (empty account) — a warm nudge, not a wall. */
.firstrun { border-color: var(--orange); }
.firstrun h2 { color: var(--orange-press); }

/* Danger zone — a collapsed panel at the very bottom; the summary and button read
   "careful here" without shouting. */
.danger-zone > summary { cursor: pointer; font-family: var(--font-display); font-weight: 700; color: var(--orange-press); }
button.danger { color: var(--orange-press); border-color: var(--orange-press); }
button.danger:hover { background: var(--orange-100); }

/* ==========================================================================
   Groups — the "session card" from §02, adapted for a narrow screen
   ========================================================================== */
.group {
  background: var(--white);
  border: var(--outline);
  border-radius: var(--r-card);
  box-shadow: var(--lift);
  overflow: hidden;
  margin: var(--s2) 0;
}
.group-head {
  display: flex; align-items: center; gap: var(--s1);
  padding: 12px 14px;
  background: var(--panel);
  border-bottom: var(--outline);
}
.group-head b { flex: 1; font-family: var(--font-display); font-weight: 600; font-size: 15px; min-width: 0; }
.group-head .count {
  flex: none;
  font-size: 11px; font-weight: 700;
  color: var(--green-press);
  background: var(--green-100);
  border-radius: var(--r-pill);
  padding: 4px 9px;
}

.dot {
  width: 10px; height: 10px; border-radius: var(--r-pill); flex: none;
  border: 1.5px solid var(--ink);
}

.group ul { list-style: none; margin: 0; padding: 10px; display: grid; gap: 8px; }
.group li {
  background: var(--cream);
  border: var(--hair);
  border-radius: 12px;
  padding: 10px 12px;
}
.group li a {
  color: var(--ink); text-decoration: none; word-break: break-word;
  font-size: 14px; font-weight: 600;
}
.group li a:hover { color: var(--orange-link); text-decoration: underline; }

/* ==========================================================================
   Notes
   ========================================================================== */
#notesWrap h2 { font-size: 17px; margin: var(--s4) 0 var(--s1); }
.note-row { display: flex; align-items: flex-start; gap: var(--s1); }
.note-row .note { flex: 1; }
#notes .note {
  background: var(--white);
  border: var(--outline);
  border-radius: 12px;
  box-shadow: var(--lift-sm);
  padding: 11px 13px;
  margin: var(--s1) 0;
  white-space: pre-wrap;
  font-size: 14px;
}

/* ==========================================================================
   Add bar
   ========================================================================== */
.addbar input, .addbar select { margin-bottom: var(--s1); }
.addbar .row { margin: 0; }
.addbar select { flex: 1; width: auto; margin: 0; }

/* The rows stack with no margin of their own, and every button casts a solid
   offset shadow — so without this the "Save link" shadow lands on top of the
   "New note" button below it. */
.addbar .row + .row { margin-top: var(--s1); }

/* The row above ends flush with the card (the select is flex:1). These two must
   fill the width the same way, or the card's right edge looks ragged. */
.addbar .row:last-child > button { flex: 1; }

/* ==========================================================================
   Get-the-extension
   ========================================================================== */
.get-ext { margin: var(--s2) 0 0; font-size: 13px; }
.get-ext a { color: var(--orange-link); font-weight: 600; text-decoration: none; }
.get-ext a:hover { text-decoration: underline; }

/* .btn-store lived here for the "go install the extension" empty state, which is
   gone — the web app writes to Drive on its own now, so an empty account gets a
   workspace rather than a store link. The .get-ext text link above is the one
   remaining pointer to the extension, and it is deliberately quieter. */

/* ==========================================================================
   Sharing
   ========================================================================== */
.token-box, .bm-code {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--dark-text);
  background: var(--dark-bg);
  border: var(--outline);
  border-radius: 10px;
  padding: 10px;
}
.token-box { margin-top: var(--s1); max-height: 110px; overflow: auto; word-break: break-all; white-space: pre-wrap; }
.bm-code { flex: 1; overflow: auto; white-space: nowrap; }

.share-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 0;
  border-top: var(--hair);
}
.share-row:first-child { border-top: none; }
.share-row .mini { margin-left: 4px; }

/* ==========================================================================
   Mailbox
   ========================================================================== */
#mailCard textarea { margin-bottom: var(--s1); }
#mailCard .row { margin: 0 0 var(--s1); }

/* Passcode row: the field takes the width, "New" stays compact; a hint sits under it. */
.mail-pass-row { display: flex; gap: 8px; align-items: center; }
.mail-pass-row #mailPass { flex: 1; }
#mailGenPass { flex: none; white-space: nowrap; }
.mail-pass-hint { font-size: 12px; margin: 4px 0 12px; }

/* Collapsed by default so it doesn't crowd the signed-in page; a +/− marker and an
   optional "N waiting" count on the summary make it clearly openable. */

/* The passcode carries the whole secret, so it gets the width; the expiry select
   is a fixed choice and shouldn't compete with it. */
#mailPass { flex: 2 1 180px; width: auto; }
#mailExp  { flex: 1 1 130px; width: auto; }
#mailSend { flex: 1 1 130px; }

.mail-label {
  display: block;
  margin: var(--s3) 0 0;
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
  color: var(--ink-soft);
}
.mail-label--warn { color: var(--orange-link); }

/* the driving-truck loader — shown while a file encrypts/uploads */
/* Centred overlay, same language as the drop target: one place where "the app is doing
   something" lives. Blocking on purpose — a second click on Put in mailbox mid-upload
   would file the message twice. */
.busy {
  position: fixed; inset: 0; z-index: 70;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  padding: 24px;
}
.busy.hidden { display: none; }
.busy-inner {
  display: flex; flex-direction: column; align-items: center; gap: 13px;
  padding: 30px 34px;
  max-width: 92vw; text-align: center;
  background: var(--white); border: var(--outline); border-radius: 20px;
}
.busy-truck { position: relative; flex: none; width: 52px; height: 30px; }
.busy-svg { display: block; width: 52px; height: 30px; animation: busyBob .55s ease-in-out infinite; }
@keyframes busyBob { 50% { transform: translateY(-2px) rotate(-1.5deg); } }
.busy-road {
  position: absolute; left: -6px; right: -6px; bottom: 0; height: 2px;
  background-image: repeating-linear-gradient(90deg, var(--ink-muted) 0 6px, transparent 6px 12px);
  animation: busyRoad .35s linear infinite;
}
@keyframes busyRoad { to { background-position-x: -12px; } }
.busy-text { font-family: var(--font-display); font-weight: 600; font-size: 14px; color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .busy-svg, .busy-road { animation: none; }
}

/* attach-a-file affordance in the composer */
.mail-attach-row { margin-top: 0; }
/* One attachment or twenty: the same chip, stacked. A batch is a list rather than a
   summary line because the sender needs to see WHICH files are going, and to pull one
   back out without starting over. */
.mail-file-list { margin: 6px 0 2px; display: flex; flex-direction: column; gap: 4px; }
.mail-file-total {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; padding: 0 4px;
}
.mail-file-clear-all {
  margin-left: auto; flex: none;
  min-height: 0; padding: 2px 10px; font-size: 12px; border-radius: 8px;
}

.mail-file-chip {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  background: var(--green-100);
  border: var(--outline);
  border-radius: 11px;
  font-size: 13px;
}
.mail-file-name { font-weight: 600; color: var(--ink); word-break: break-all; flex: 1; min-width: 0; }
.mail-file-size { flex: none; }
.mail-file-x {
  flex: none; min-height: 0; padding: 2px 9px; font-size: 16px; line-height: 1;
  border-radius: 8px;
}
/* Everything else in this app is a 44px target on purpose; these × buttons opted out
   with min-height: 0, and they sit in the attached-files list — the control someone
   reaches for right after fat-fingering the file picker on a phone. */
@media (max-width: 720px) {
  .mail-file-x { min-height: 40px; min-width: 40px; }
}

/* The at-file-time passcode echo. A sticker-outlined slip that reads as its own thing,
   set off with the accent tint so the owner catches it before it's gone — it can't be
   recovered later. */
.mail-lastpass {
  margin-top: var(--s3);
  padding: var(--s1) var(--s2);
  background: var(--orange-100);
  border: var(--outline);
  border-radius: 12px;
  box-shadow: var(--lift-sm);
}
.mail-lastpass .mail-label { margin-top: 0; }
.mail-lastpass .row { margin-top: var(--s1); }
.mail-lastpass-note {
  margin: var(--s1) 0 0;
  font-size: 13px; font-weight: 600;
  color: var(--orange-link);
}

#mailIdWrap .row { margin-top: var(--s1); }
#mailIdWrap .row > button { flex: 1; }

.mail-qr {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin-top: var(--s1);
}
/* A hard-coded light ground, not var(--white) — this card must stay pale even on the
   dark theme, because the QR inside it is dark-on-light and a scanner that meets an
   inverted code often just refuses. The code has to work at night too. */
/* the short code: claim form + the code readout */
.mail-code {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: var(--s1) 0 var(--s2);
  padding: 10px 12px;
  background: var(--green-100); border: var(--outline); border-radius: 12px;
}
.mail-code-label { flex-basis: 100%; font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.mail-code-val {
  flex: 1; min-width: 0;
  font-family: var(--font-mono); font-size: 20px; font-weight: 700; letter-spacing: .02em;
  color: var(--ink); word-break: break-all;
}
.mail-claim { margin: var(--s1) 0 var(--s2); }
.mail-claim .row { margin: 6px 0 0; }
#mailHandle { flex: 1; }
#mailClaimMsg { min-height: 16px; margin: 6px 0 0; }
#mailClaimMsg.err { color: var(--orange-press); }

.mail-qr-img {
  flex: none;
  background: #FBF6EC;
  border: var(--outline);
  border-radius: 12px;
  box-shadow: var(--lift-sm);
  padding: 8px;
  line-height: 0;
}
.mail-qr-img svg { display: block; width: 148px; height: 148px; }

.mail-qr-say { flex: 1 1 220px; min-width: 0; }
.mail-qr-say p { margin: 0 0 6px; font-size: 13px; }
.mail-qr-say .row { margin-top: var(--s1); }

.mail-row {
  display: flex; align-items: flex-start; gap: var(--s1);
  padding: 11px 0;
  border-top: var(--hair);
}
.mail-row:first-child { border-top: none; }
.mail-body { flex: 1; min-width: 0; }

/* Expiry + the short code this item lives at, on one quiet line under the preview. */
.mail-row-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 4px; font-size: 12px; }
.mail-row-dot { opacity: 0.6; }
.mail-row-code {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--orange-link);
  background: var(--orange-100);
  border: none;
  border-radius: 6px;
  padding: 1px 8px;
  cursor: pointer;
}
.mail-row-code:hover { text-decoration: underline; }

/* Two lines of the message, then it stops. This is an outbox, not a reader — the
   point is to recognise which message this is, not to re-read it. */
.mail-preview {
  font-size: 14px;
  white-space: pre-wrap;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.shared-box {
  margin-top: var(--s2);
  background: var(--cream);
  border: var(--outline);
  border-radius: 14px;
  padding: 14px;
}
.shared-box ul { list-style: none; margin: var(--s1) 0 0; padding: 0; }
.shared-box li { padding: 8px 0; border-top: var(--hair); }
.shared-box li:first-child { border-top: none; }
.shared-box a { color: var(--ink); text-decoration: none; word-break: break-word; font-weight: 600; }
.shared-box a:hover { color: var(--orange-link); text-decoration: underline; }

/* ==========================================================================
   Bookmarklet
   ========================================================================== */
.bm summary { cursor: pointer; font-family: var(--font-display); font-weight: 600; }
.bm-btn {
  display: inline-block;
  font-family: var(--font-display); font-weight: 600; font-size: 14px;
  color: #fff; background: var(--orange);
  border: var(--outline);
  border-radius: 12px;
  box-shadow: var(--lift-orange);
  padding: 11px 16px;
  text-decoration: none;
  cursor: grab;
}

details summary { cursor: pointer; }
.conn-details { margin-top: var(--s2); }
.origin-info { word-break: break-all; }

/* The chevron is baked into a data-URI, so it can't follow --ink. In dark mode
   --ink flips to cream, and a dark chevron would vanish — hence its own variant. */
:root[data-theme="dark"] select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 10 10'%3E%3Cpath d='M2 4l3 3 3-3' fill='none' stroke='%23FBF6EC' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* dark: green brightens, so text on a green fill must go near-black */
:root[data-theme="dark"] .group-head .count { color: var(--green-ok-text); }

/* theme cycle button (the PWA has no options page, so the control lives here) */
.theme-cycle { font-size: 15px; line-height: 1; min-width: 40px; }

/* ==========================================================================
   Phones — this app is used on the phone, so tune the touch surface.
   ========================================================================== */
@media (max-width: 720px) {
  /* 16px inputs stop iOS from zooming the page in on focus */
  input[type=text], input[type=url], input[type=password], select { font-size: 16px; }

  /* roomier tap targets for the small header/share controls */
  .mini { min-height: 40px; padding: 8px 12px; }
}

/* Chunked into fours for the one person who genuinely has to key it in. word-spacing
   widens the gaps between groups so the eye can hold its place; the copy button still
   copies the REAL id, spaces and all removed. */
.mail-id-chunks { word-spacing: 0.5em; letter-spacing: 0.04em; font-size: 12px; }

/* Numbered passcode series. Deliberately quiet — it sits under the passcode box and
   should read as a refinement of it, not a second feature competing for attention. */
.mail-series {
  align-items: center; gap: 8px; flex-wrap: wrap;
  margin: -6px 0 8px; font-size: 12px;
}
.mail-series label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
.mail-series input[type="text"] { flex: 0 1 130px; min-width: 0; }
.mail-series input:disabled { opacity: .5; }
.mail-series label:has(input:disabled) { opacity: .5; cursor: default; }
#mailSeriesN { font-variant-numeric: tabular-nums; }
/* The one place in the composer that earns a warning colour: plain numbering means a
   recipient can open the others, and that has to be visible before you send. */
.mail-series-warn {
  font-size: 12px; margin: 0 0 12px; padding: 8px 10px;
  border-radius: 8px; max-width: 62ch;
  color: var(--ink); background: color-mix(in srgb, var(--orange) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--orange) 40%, transparent);
}

/* ==========================================================================
   App tabs — Send / Tabs & bookmarks
   Two unrelated jobs sharing an account. Peers, not a panel buried under a list.
   ========================================================================== */
.apptabs {
  display: flex; gap: 6px;
  margin: 0 0 14px;
  padding: 5px;
  background: var(--panel);
  border: var(--outline);
  border-radius: 14px;
}
.apptab {
  flex: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-family: var(--font-display); font-weight: 600; font-size: 15px;
  color: var(--ink-soft);
  background: transparent;
  border: 0; border-radius: 10px;
  padding: 10px 12px; min-height: 42px;
  cursor: pointer;
}
.apptab:hover { color: var(--ink); }
.apptab.is-on {
  color: var(--ink);
  background: var(--white);
  box-shadow: var(--lift-sm);
}
/* Waiting messages, surfaced on the tab rather than inside a panel you'd have to open. */
.apptab-badge { font-size: 12px; font-weight: 700; color: var(--orange-link); }
.apptab-badge:empty { display: none; }

/* ==========================================================================
   Drop target
   The whole window is the target — aiming at a small strip is the friction this
   is meant to remove — and this overlay is only the cue that it will land.
   ========================================================================== */
.dropzone {
  position: fixed; inset: 0; z-index: 60;
  display: none; place-items: center;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  padding: 24px;
}
.dropzone.is-on { display: grid; }
.dropzone-inner {
  font-family: var(--font-display); font-weight: 700; font-size: 20px;
  color: var(--ink);
  background: var(--white);
  border: 3px dashed var(--orange);
  border-radius: 20px;
  padding: 42px 46px;
  text-align: center;
  max-width: 90vw;
}


/* Only after BUSY_SLOW_AFTER. A stalled upload never throws — it just sits — so without
   this the overlay is indistinguishable from a hang, with no way out. */
.busy-slow {
  margin: 2px 0 0; max-width: 34ch;
  font-size: 12.5px; line-height: 1.5; color: var(--ink-muted);
}
.busy-text { font-family: var(--font-display); font-weight: 600; font-size: 16px; }

/* The per-message passcode peek. Closed by default because an outbox is a screen other
   people glance at, and a visible passcode opens a live message. */
.mail-peek {
  font-family: var(--font-body); font-weight: 500; font-size: 12px;
  color: var(--ink-muted);
  background: none; border: 0;
  /* The base `button` rule is built for real buttons — 44px tall with a lift shadow —
     which turned this inline control into a chunky block on its own line. Unset the
     parts that made it a button; keep a little vertical padding so it is still a
     reasonable tap target inside the meta row. */
  min-height: 0; box-shadow: none;
  display: inline-flex; padding: 3px 0;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.mail-peek:hover { background: none; transform: none; box-shadow: none; }
.mail-peek:active { transform: none; box-shadow: none; }
.mail-peek:hover { color: var(--ink); }
.mail-peek.is-open {
  font-family: var(--font-mono);
  color: var(--orange-link); font-weight: 600;
}

/* Time-capsule row in the composer. */
.mail-capsule { align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.mail-capsule label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; cursor: pointer; }
.mail-capsule input[type="datetime-local"] { flex: 1 1 200px; min-width: 0; }
.mail-capsule input:disabled { opacity: .5; }
.mail-capsule-hint { font-size: 12px; margin: 6px 0 0; max-width: 60ch; line-height: 1.5; }

/* "Show N more" under the outbox. A row, not a button-looking thing — it is a
   disclosure, not an action with consequences. */
.mail-more {
  display: block; width: 100%;
  margin-top: 8px;
  color: var(--ink-soft);
  background: none; border: 0; box-shadow: none;
  min-height: 0; padding: 8px 0;
  font-size: 12.5px;
}
.mail-more:hover { background: none; color: var(--ink); transform: none; box-shadow: none; }
.mail-more:active { transform: none; box-shadow: none; }

/* Self-send. Given its own row above the passcode field, because when sender and
   receiver are the same person none of what follows is relevant — the passcode, the
   series, the expiry are all chosen for you. */
/* So it leads the panel instead of sitting inside the composer. It earns its
   weight from being a framed, tinted block — NOT from taking the orange: "Put in
   mailbox" is the composer's submit and is the one primary action in this view.
   Tinted rather than shadowed because the panel is bare on the page here, and a
   second stacked lift next to the cards below reads as noise. */
.mail-self-block {
  background: var(--panel);
  border: var(--outline);
  border-radius: var(--r-card);
  padding: 14px;
  margin: 0 0 var(--s2);
}
/* The portal as a PANEL rather than a disclosure inside Send. Same card language as the
   rest of the app; the heading row carries the "updated N min ago" state that used to sit
   on the summary. */
.portal-panel { padding: 18px; margin: var(--s2) 0; }
/* Files on the clipboard — the same chip language as the composer's attachments, because
   it is the same act. */
.clip-attach-row { margin: 10px 0 0; align-items: center; }
.clip-files { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.clip-file {
  display: flex; align-items: stretch; gap: 6px;
  background: var(--green-100); border: var(--outline); border-radius: 11px; overflow: hidden;
}
.clip-file-name {
  flex: 1; min-width: 0; text-align: left;
  padding: 9px 12px; font-size: 13px; font-weight: 600;
  background: none; border: 0; color: var(--ink); cursor: pointer; word-break: break-all;
}
.clip-file-name:hover { background: var(--white); }
.clip-file-x {
  flex: none; min-width: 40px;
  background: none; border: 0; border-left: var(--hair);
  color: var(--ink-muted); font-size: 16px; cursor: pointer;
}
.clip-file-x:hover { background: var(--orange-100); color: var(--orange-press); }

/* Two panels, two jobs — a visible gap so they never read as one control with a lot of
   options, which is exactly how the private box came to be publishable. */
.portal-panel + .portal-panel { margin-top: var(--s4); }
.portal-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.portal-head .portal-title { display: inline-flex; align-items: center; gap: 8px; font-size: 16px; }
.portal-head .portal-state { margin-left: auto; font-size: 12px; font-weight: 400; }

/* Scanning their QR. The preview is capped so a phone camera does not take over the
   screen, and mirrored off — this points at someone else's display, not your own face. */
.portal-scan-row { margin: 8px 0 0; align-items: center; }
.portal-scan { margin-top: 10px; }
.portal-scan-video {
  width: 100%; max-width: 320px; max-height: 240px;
  background: var(--ink);
  border: var(--outline); border-radius: 12px;
  object-fit: cover;
}

/* The paid lane. Understated: it is an upgrade, not the point of the panel. */
.portal-pro { margin: 10px 0 0; }
.portal-pro-set { margin-top: 8px; }
.portal-pro-on {
  margin: 0; padding: 8px 11px;
  font-size: 13px; font-weight: 600;
  color: var(--green-press); background: var(--green-100);
  border: var(--outline); border-radius: 11px;
}
:root[data-theme="dark"] .portal-pro-on { color: var(--green-ok-text); }

/* Delivery: the row that turns the portal from a private scratchpad into something a
   watcher can see. Quiet until switched on. */
.portal-deliver { margin: 10px 0 0; }
.portal-deliver-set { margin-top: 8px; }
/* The passcode being delivered to — boxed and readable, because it is the string the
   sender reads back to confirm they are pointed at the right watcher. */
.portal-deliver-pass {
  flex: 1; min-width: 0;
  font-size: 14px; word-break: break-all;
  background: var(--white); border: var(--hair); border-radius: 8px; padding: 4px 8px;
}
.portal-deliver-on {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 11px;
  background: var(--green-100); border: var(--outline); border-radius: 11px;
  font-size: 13px;
}
:root[data-theme="dark"] .portal-deliver-on { color: var(--ink); }

/* "keep this updated" sits beside the passcode peek in an outbox row and points at the
   portal box — the one editor for a delivered slot. */
.mail-again { font-weight: 600; }

/* THE PORTAL. Same panel family as the self-send block above it — it is the same job
   ("me, elsewhere") one step further — but collapsed by default, because most visits are
   a send and an open textarea at the top of the panel reads as the thing to type into. */
.portal-block {
  background: var(--panel);
  border: var(--outline);
  border-radius: var(--r-card);
  padding: 14px;
  margin: 0 0 var(--s2);
}
.portal-block > summary {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  cursor: pointer;
  font-family: var(--font-display); font-weight: 600; font-size: 15px;
}
.portal-title { display: inline-flex; align-items: center; gap: 8px; }
.portal-state { margin-left: auto; font-size: 12px; font-weight: 400; }
.portal-lede { margin: 10px 0; }
.portal-block textarea { background: var(--white); }
.portal-actions { margin-bottom: 0; }

/* The three ways into a portal. Numbered, because they are alternatives ranked by which
   one works everywhere — the address first (any device with a keyboard), the QR second
   (needs a camera), and "you may already be there" third. The old panel offered only the
   QR, which quietly assumed a phone at both ends. */
.portal-open { margin-top: var(--s2); }
.portal-ways { margin: 0; padding-left: 20px; display: grid; gap: 12px; font-size: 13px; line-height: 1.5; }
.portal-ways li { padding-left: 2px; }
.portal-ways .row { margin: 8px 0 0; }
.portal-url {
  display: inline-block;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 13px;
  background: var(--white); border: var(--hair); border-radius: 8px;
  padding: 2px 7px;
  word-break: break-all;
}
.portal-open .mail-qr-img { margin-top: 8px; }
/* .mail-self-note is centred for the QR panel it was written for; in a left-aligned
   numbered list that reads as a stray indent. */
.portal-open .mail-self-note { text-align: left; margin-top: 12px; }

/* Both versions kept. Orange rather than red: nothing went wrong and nothing was lost —
   there is just tidying to do. */
.portal-conflict {
  margin: 10px 0 0;
  padding: 10px 12px;
  font-size: 13px; line-height: 1.45;
  background: var(--orange-100);
  border: var(--outline);
  border-radius: 11px;
}

/* Arrived here from the landing page's "Send to my phone" — say which block that was
   about. A ring that fades rather than a permanent state: it answers "where am I" once
   and then gets out of the way. Motion-free, so it costs nothing to anyone who has
   asked for reduced motion. */
.mail-self-block.is-called-out {
  border-color: var(--orange);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--orange) 22%, transparent);
  transition: box-shadow 400ms var(--ease-fast), border-color 400ms var(--ease-fast);
}
.mail-self { align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.mail-self-block .mail-self { margin-top: 0; }
.mail-self-hint { font-size: 12px; flex: 1 1 200px; min-width: 0; }

/* The seam between the two paths: one tap to my own phone, or the form below to
   hand it to someone else. A ruled label, so the composer reads as the second
   option rather than as the only thing on the screen. */
.mail-or {
  display: flex; align-items: center; gap: 10px;
  margin: var(--s3) 0 var(--s2);
  font-size: 12px;
}
.mail-or::before, .mail-or::after { content: ""; flex: 1; border-top: var(--hair); }
.mail-self-out {
  margin: 12px 0;
  padding: 18px;
  text-align: center;
  background: var(--white); border: var(--outline); border-radius: 16px;
}
.mail-self-out .mail-qr-img { display: inline-block; }
.mail-self-note { margin: 12px 0; font-size: 12.5px; color: var(--ink-soft); max-width: 40ch; margin-inline: auto; }
.mail-self-out .row { justify-content: center; }

/* ==========================================================================
   Tags — the overlapping axis on top of exclusive groups
   ========================================================================== */
.tag-bar { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 var(--s2); }
.tag-chip {
  font-family: var(--font-body); font-size: 12px; font-weight: 600;
  color: var(--ink-soft);
  background: var(--white); border: var(--hair); border-radius: var(--r-pill);
  padding: 5px 11px; min-height: 0; box-shadow: none;
  cursor: pointer;
}
.tag-chip:hover { background: var(--panel); color: var(--ink); transform: none; box-shadow: none; }
.tag-chip:active { transform: none; box-shadow: none; }
.tag-chip.is-on { color: var(--white); background: var(--ink); border-color: var(--ink); }

/* On a card: the tag and its remove control read as one chip. */
.group-tags { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 14px 0; }
.tag-chip--on-card { display: inline-flex; align-items: stretch; padding: 0; overflow: hidden; }
.tag-pick, .tag-x {
  font: inherit; color: inherit;
  background: none; border: 0; box-shadow: none; min-height: 0;
  cursor: pointer;
}
.tag-pick { padding: 5px 4px 5px 11px; }
.tag-x { padding: 5px 9px 5px 4px; color: var(--ink-muted); }
.tag-x:hover { color: var(--orange-link); background: none; transform: none; box-shadow: none; }
.tag-pick:hover { background: none; transform: none; box-shadow: none; }

/* Someone who arrived by tapping "Reply" on a message they just read. They have never
   sent anything, so the composer needs to say what they are doing here. */
.reply-banner {
  margin: 0 0 var(--s2);
  padding: 13px 15px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--green) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--green) 45%, transparent);
}
.reply-banner b { font-family: var(--font-display); font-size: 15px; }
.reply-banner p { margin: 4px 0 0; font-size: 13px; line-height: 1.5; }

/* Self-test results — one line per link in the chain, so a failure names which. */
.selftest summary { cursor: pointer; font-family: var(--font-display); font-weight: 700; }
.selftest-out { margin: var(--s2) 0 0; padding-left: 20px; display: grid; gap: 6px; }
.selftest-out:empty { display: none; }
.selftest-step { font-size: 13px; line-height: 1.5; }
.selftest-step b { font-family: var(--font-display); }
.selftest-step.is-ok b { color: var(--green-press); }
.selftest-step.is-bad b { color: var(--orange-press); }
.selftest-step span { color: var(--ink-soft); }
