/* ============================================================
   Selu — warm dark, typographic, not a dashboard.
   Fonts are system stacks on purpose: nothing is fetched, so
   the app renders identically offline and inside Capacitor.
   ============================================================ */

:root {
  /* warm blacks — browns, never blue-grey */
  --ink-0: #100c0a;
  --ink-1: #17110e;
  --ink-2: #1e1713;
  --ink-3: #271e19;
  --rule:  #322620;
  --rule-soft: #241c17;

  /* warm paper */
  --paper:  #f3ebe1;
  --paper-2:#c4b3a3;
  --paper-3:#8a7867;
  --paper-4:#645547;

  --ember:  #ef7d4e;
  --ember-2:#ffa070;
  --gold:   #e3b25f;
  --rose:   #d9707a;
  --moss:   #93a06a;
  --plum:   #a97fb9;
  --clay:   #c98a5a;

  --accent: var(--ember);

  --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;

  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);

  /* The app is a phone app. On anything wider it does not stretch — it
     stays a phone-width column in the middle of the screen. --app-w is the
     widest phone we target (iPhone Pro Max), so the column never renders
     wider than a real device would.

     --col is that column's actual width, and every size that used to be
     written in vw is written against it instead. vw is the window, which
     stops being the app the moment there is a window. */
  --app-w: 430px;
  --col: min(100vw, var(--app-w));

  /* the world outside the column */
  --void: #080605;

  /* The bar at the bottom is on every screen and is always the same, so it
     is the one piece of furniture every screen has to clear. */
  --bar-h: 54px;
  --bar-total: calc(var(--bar-h) + var(--safe-b));

  /* the camera's own bottom row, so the things that must clear it can say
     so instead of guessing */
  --cam-row-b: 20px;

  /* how much furniture the bottom of the current pane is holding, measured
     from the bottom of the pane — which already sits above the bar */
  --bottom-furniture: 0px;

  --ease: cubic-bezier(.22,.68,.26,1);
}

/* per-album accents, assigned by id hash */
.acc-0 { --accent: var(--ember); }
.acc-1 { --accent: var(--gold);  }
.acc-2 { --accent: var(--rose);  }
.acc-3 { --accent: var(--moss);  }
.acc-4 { --accent: var(--plum);  }
.acc-5 { --accent: var(--clay);  }

* { box-sizing: border-box; }

/* The hidden attribute must win.
   A class rule like `.cam-block { display: grid }` beats the browser's own
   `[hidden] { display: none }`, so setting el.hidden = true changed nothing:
   the camera's message panel stayed on screen as an empty near-black sheet
   covering the preview, with the buttons showing through above it. Photos
   worked the whole time, because the camera was never the problem. */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
/* html keeps the app's own black: it is what an iOS rubber-band overscroll
   reveals, and what the status bar is tinted to. Only body carries the void,
   and on a phone body is covered edge to edge by the panes. */
html { background: var(--ink-0); -webkit-text-size-adjust: 100%; }

body {
  background: var(--void);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

/* ---------- the three panes ---------- */

/* Constraining the shell, not each piece of furniture: everything the panes
   position absolutely — the capture controls, the camera rail, the chip —
   is measured against .pane, so capping the shell puts all of it inside the
   column with no per-element arithmetic. inset:0 plus auto inline margins
   centres it without a transform, which would otherwise capture every
   position:fixed descendant. */
#pager {
  position: fixed; top: 0; left: 0; right: 0;
  bottom: var(--bar-total);
  overflow: hidden;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  /* Off-screen while the column fills the window, so no breakpoint is
     needed to switch it on. */
  box-shadow: 0 0 0 1px var(--rule-soft), 0 0 90px 10px rgba(0,0,0,.55);
}
#track { display: flex; height: 100%; will-change: transform; }

.pane {
  flex: 0 0 100%; width: 100%; height: 100%;
  position: relative; overflow: hidden;
  /* vertical scrolling stays native; horizontal belongs to the pager */
  touch-action: pan-y;
}

/* A desktop scrollbar would take a 15px gutter out of the column and put a
   chrome the phone does not have down the middle of the screen. Phones
   already overlay theirs, so this only changes the wide case. */
.pane-scroll, .sheet, .cam-block, .viewer-msg { scrollbar-width: none; }
.pane-scroll::-webkit-scrollbar,
.sheet::-webkit-scrollbar,
.cam-block::-webkit-scrollbar,
.viewer-msg::-webkit-scrollbar { display: none; }

.pane-scroll {
  height: 100%; overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain;
  background:
    radial-gradient(120% 42% at 50% 0%, #221812 0%, rgba(34,24,18,0) 62%),
    var(--ink-0);
}

#albums-scroll { padding-bottom: 24px; }

/* The camera holds furniture over the bottom of its own pane; the other two
   screens end flush against the bar. */
body[data-pane="camera"] { --bottom-furniture: 150px; }

.plain-top {
  display: flex; align-items: center; gap: 14px;
  padding: calc(var(--safe-t) + 14px) 20px 12px; min-height: 56px;
}
/* The chat header had a search box between the avatar and the plus, and it
   was what held them apart. With it gone the spacer does that job — .grow
   was only ever defined inside a sheet, so it needs saying here too. */
.plain-top .grow { flex: 1; }

button, input, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: var(--accent); }
/* Every picture and every clip in the app.
 *
 * Long-pressing one was opening the WebView's own menu — copy image, save
 * image — which is the wrong menu on top of an app whose whole point is
 * that what you capture stays inside it. Worse, the native callout CANCELS
 * the touch sequence, so the app's own long-press never completed: on a
 * photo in a chat you got the browser's menu instead of "delete".
 *
 * Declared once, on the elements themselves, so it covers everything drawn
 * from a blob without each screen having to remember: album grids, the
 * cover at the top of an album, sub-album covers, the shelf, the photo
 * viewer, the send picker, message tiles, profile pictures, and the live
 * camera preview.
 *
 * -webkit-touch-callout is the iOS/WKWebView one and does the real work
 * there; Chromium does not implement it at all, which is why the
 * contextmenu event is refused in JS as well rather than instead.
 *
 * This stops the IN-PAGE menu. It cannot stop a screenshot, or the
 * browser's own chrome, and it is not a claim that a picture cannot leave
 * the phone — only that the app does not hand it over itself. */
img, video {
  display: block; max-width: 100%;
  -webkit-touch-callout: none;
  -webkit-user-select: none; user-select: none;
  -webkit-user-drag: none; -khtml-user-drag: none;
}
:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; border-radius: 3px; }

/* ---------- shell ---------- */

#topbar {
  position: sticky; top: 0; z-index: 30;
  padding: calc(var(--safe-t) + 14px) 20px 12px;
  background: linear-gradient(var(--ink-0) 58%, rgba(16,12,10,0));
  display: flex; align-items: center; gap: 14px; min-height: 56px;
}
#topbar.solid {
  background: rgba(16,12,10,.88);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--rule-soft);
}

#view { padding: 0 0 40px; }
.wrap { padding: 0 20px; }

/* ---------- type ---------- */

.eyebrow {
  font-family: var(--sans);
  font-size: 10.5px; font-weight: 600;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--paper-3);
}

h1.page {
  font-family: var(--serif);
  font-size: clamp(30px, calc(var(--col) * .08), 40px);
  line-height: 1.04; letter-spacing: -.025em;
  font-weight: 600; margin: 0;
}
.lede {
  font-family: var(--serif); font-size: 18px; line-height: 1.45;
  color: var(--paper-2); font-style: italic;
  margin: 12px 0 0; max-width: 30ch;
}
.meta {
  font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--paper-3); font-weight: 500;
}
.tnum { font-variant-numeric: tabular-nums; }

/* ---------- icon buttons ---------- */

.iconbtn {
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; color: var(--paper-2);
  border: 1px solid transparent; flex: none;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.iconbtn svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.iconbtn:hover { background: var(--ink-2); color: var(--paper); }
.iconbtn.on { color: var(--ember); }

/* backup state, said with one dot rather than a word */
.cloudbtn { position: relative; }
.cloudbtn .pip {
  position: absolute; right: 6px; top: 7px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--paper-4);
  border: 1.5px solid var(--ink-0);
  transition: background .2s var(--ease);
}
.cloudbtn[data-state="idle"] .pip    { background: var(--moss); }
.cloudbtn[data-state="syncing"] .pip { background: var(--gold); animation: pulse 1.6s var(--ease) infinite; }
.cloudbtn[data-state="offline"] .pip { background: var(--paper-4); }
.cloudbtn[data-state="error"] .pip   { background: var(--rose); }
.cloudbtn[data-state="off"] .pip     { background: transparent; border-color: var(--rule); }
.cloudbtn.has-pending[data-state="idle"] .pip { background: var(--gold); }
.spacer { flex: 1; }

.textbtn {
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 600; color: var(--paper-2); padding: 9px 2px;
}
.textbtn:hover { color: var(--paper); }
.textbtn.danger { color: var(--rose); }

/* ---------- running now ---------- */

.sec-label { padding: 22px 20px 12px; }
.sec-label.ruled { border-top: 1px solid var(--rule-soft); margin-top: 8px; }
.sec-label .eyebrow { display: block; }

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(239,125,78,.5); }
  70%  { box-shadow: 0 0 0 9px rgba(239,125,78,0); }
  100% { box-shadow: 0 0 0 0 rgba(239,125,78,0); }
}
@media (prefers-reduced-motion: reduce) { .dot { animation: none !important; } }

/* Capturing / Not capturing / Closed, above an album's name. The band that
   used to own this is gone; the album screen is the last thing using it. */
.live-tag {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--accent); margin-bottom: 10px;
}
.live-tag .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none;
  animation: pulse 2.6s var(--ease) infinite;
}

.tree { padding: 0 16px; }
.branch {
  border: 1px solid var(--rule); border-radius: 12px;
  padding: 11px; margin-bottom: 12px;
}

.branch-head { display: flex; align-items: center; gap: 8px; }
.branch-open { flex: 1; min-width: 0; text-align: left; }
.branch-name {
  display: block; font-family: var(--serif); font-size: 20px;
  letter-spacing: -.018em; color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.branch-head .meta { display: block; margin-top: 2px; }

/* The indent and the rule are the nesting. Nothing else says it. */
.nest {
  margin-top: 11px; padding-left: 12px;
  border-left: 1px solid var(--rule);
}
.branch > .livecard { margin: 0; }

/* The album that is capturing, with its most recent frame. */
.livecard {
  position: relative; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--rule); background: var(--ink-1);
}
.livecard.is-live { border-color: var(--accent); }
.livecard-open { display: block; width: 100%; text-align: left; }
.livecard-cover {
  display: block; position: relative; height: 96px;
  background: color-mix(in srgb, var(--accent) 14%, var(--ink-2));
}
.livecard-cover img { width: 100%; height: 100%; object-fit: cover; }
.livecard-body { display: block; padding: 9px 11px; }
.livecard-name {
  display: flex; align-items: center; gap: 7px;
  font-family: var(--serif); font-size: 17px; letter-spacing: -.015em;
  color: var(--paper); min-width: 0;
}
.livecard-name .dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent); animation: pulse 2.6s var(--ease) infinite;
}
.livecard-body .meta { display: block; margin-top: 3px; }
.livecard > .iconbtn {
  position: absolute; right: 4px; bottom: 6px;
  width: 34px; height: 34px;
}
.livecard > .iconbtn svg { width: 18px; height: 18px; }

/* A sub-album that is not the one capturing. */
.nest-row {
  display: flex; align-items: center; gap: 9px; width: 100%;
  text-align: left; padding: 8px 4px 8px 0;
}
.nest-row .sq {
  width: 32px; height: 32px; border-radius: 7px; flex: none;
  background: color-mix(in srgb, var(--accent) 20%, var(--ink-3));
}
.nest-text { min-width: 0; flex: 1; }
.nest-name {
  display: block; font-family: var(--serif); font-size: 15px;
  letter-spacing: -.012em; color: var(--paper-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nest-row .meta { display: block; margin-top: 1px; }

.idle-note {
  display: flex; align-items: center; gap: 10px; width: 100%;
  text-align: left; margin: 2px 0 4px; padding: 12px 20px;
  font-size: 13.5px; line-height: 1.4; color: var(--paper-3);
}
.idle-note .dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--paper-4);
}

/* ---------- closed ---------- */

.grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px 12px;
  padding: 0 16px 16px;
}
.gcard { display: block; text-align: left; min-width: 0; }
.gcover {
  display: block; position: relative;
  aspect-ratio: 4/3; border-radius: 9px; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 12%, var(--ink-2));
  border: 1px solid var(--rule-soft);
}
.gcover img { width: 100%; height: 100%; object-fit: cover; }
.gcover .badge { position: absolute; left: 6px; bottom: 6px; }
.gname {
  display: block; margin-top: 7px;
  font-family: var(--serif); font-size: 16px; letter-spacing: -.015em;
  color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gmeta { display: block; margin-top: 2px; }

/* ---------- choosing between two outcomes ----------
   A card per answer rather than a row of buttons: each one needs a sentence
   about what happens to the photos, and a sentence does not fit on a pill. */

.choice-card {
  display: block; width: 100%; text-align: left;
  padding: 15px 16px; margin-bottom: 10px;
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--ink-2);
}
.choice-card:hover { border-color: var(--paper-4); }
.choice-card:active { transform: scale(.99); }
.choice-card.danger { border-color: color-mix(in srgb, var(--rose) 40%, var(--rule)); }
.choice-title {
  display: block; font-family: var(--serif); font-size: 18px;
  letter-spacing: -.01em; color: var(--paper); margin-bottom: 4px;
}
.choice-card.danger .choice-title { color: var(--rose); }
.choice-sub {
  display: block; font-size: 13px; line-height: 1.45; color: var(--paper-3);
}

/* ---------- inside an album ---------- */

.album-top {
  display: grid; justify-items: center; gap: 12px;
  padding: 14px 24px 22px;
}

/* The album's face. A circle, because it is standing in for the album the
   way a profile picture stands in for a person. */
.album-cover {
  width: 104px; height: 104px; border-radius: 50%;
  overflow: hidden; display: block;
  background: color-mix(in srgb, var(--accent) 22%, var(--ink-2));
  border: 1px solid var(--rule);
}
.album-cover img { width: 100%; height: 100%; object-fit: cover; }

.album-name {
  font-family: var(--serif);
  font-size: clamp(26px, calc(var(--col) * .085), 36px);
  line-height: 1.08; letter-spacing: -.025em; font-weight: 600;
  margin: 0; text-align: center; text-wrap: balance;
}

.album-people {
  font-size: 13.5px; line-height: 1.4; color: var(--paper-3);
  text-align: center; max-width: 34ch;
}
.album-people:hover { color: var(--paper-2); }

/* Everything in the album, newest first, all three kinds in one grid. */
.album-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 3px; padding: 0 3px 24px;
}
/* lazyList keeps a sentinel at the end; it must not take a cell. */
.album-grid > [aria-hidden="true"] { grid-column: 1 / -1; }

/* The cell, not the tile, is the grid child now: the X has to be a sibling
   of the photo rather than a button inside a button. Same shape the album
   card on the shelf already uses. */
.gcell { position: relative; aspect-ratio: 1; }
.gcell .gtile { width: 100%; height: 100%; aspect-ratio: auto; }

.gtile {
  position: relative; display: block;
  aspect-ratio: 1; overflow: hidden;
  background: var(--ink-2);
}

/* Take this back out of a shared album.
   Top left, because top right is where a video already puts its badge and
   its length. Small, but with a hit area bigger than the mark it draws:
   the consequence lands on other people's phones and a fat thumb on a
   three-across grid of photographs should not reach it by accident. */
.gtile-x {
  position: absolute; left: 0; top: 0; z-index: 2;
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; background: none; -webkit-tap-highlight-color: transparent;
}
.gtile-x::before {
  content: ''; position: absolute; left: 4px; top: 4px;
  width: 20px; height: 20px; border-radius: 50%;
  background: rgba(16,12,10,.55); backdrop-filter: blur(2px);
}
.gtile-x svg {
  position: relative; width: 12px; height: 12px;
  fill: none; stroke: var(--paper); stroke-width: 2.2; stroke-linecap: round;
}
.gtile-x:active::before { background: rgba(16,12,10,.8); }
.gtile img { width: 100%; height: 100%; object-fit: cover; }
.gtile-play {
  position: absolute; right: 6px; top: 6px;
  width: 18px; height: 18px; display: grid; place-items: center;
}
.gtile-play svg { width: 13px; height: 13px; fill: var(--paper); filter: drop-shadow(0 1px 3px rgba(0,0,0,.8)); }
.gtile-len {
  position: absolute; right: 6px; bottom: 5px;
  font-size: 10.5px; font-weight: 600; color: var(--paper);
  text-shadow: 0 1px 4px rgba(0,0,0,.9);
}
.gtile-gone { display: grid; place-items: center; height: 100%; color: var(--paper-4); }
.gtile-gone svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.5; }

/* An album nested inside this one: a cell like any other, in the same grid.
   It was a band of its own above the photos, left-aligned and on its own
   gutters, which read as a different screen bolted on top of this one. */
.gtile-album {
  display: grid; align-content: center; justify-items: center;
  gap: 3px; padding: 6px 5px; background: var(--ink-1);
}
.gtile-albumcover {
  width: 58%; aspect-ratio: 1; border-radius: 50%;
  overflow: hidden; display: block;
  background: color-mix(in srgb, var(--accent) 26%, var(--ink-2));
  border: 1px solid var(--rule);
}
.gtile-albumcover img { width: 100%; height: 100%; object-fit: cover; }
.gtile-album:active .gtile-albumcover { transform: scale(.94); }
.gtile-albumname {
  font-size: 11.5px; line-height: 1.2; color: var(--paper); text-align: center;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  padding: 0 2px;
}
.gtile-albummeta { font-size: 10px; line-height: 1.1; }

/* A duration on a cover, with the play glyph inside it. */
.badge .play { position: static; display: inline; }

.crumb {
  font-family: var(--serif); font-size: 16px; letter-spacing: -.01em;
  color: var(--paper); flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ---------- the login gate ----------
   Above everything, opaque, and the only thing on screen. */

.gate {
  position: fixed; inset: 0; z-index: 110;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  background:
    radial-gradient(120% 46% at 50% 0%, #241a13 0%, rgba(36,26,19,0) 62%),
    var(--ink-0);
  overflow-y: auto; overscroll-behavior: contain;
  animation: fade .25s var(--ease);
}
.gate-inner {
  min-height: 100%;
  padding: calc(var(--safe-t) + 40px) 24px calc(var(--safe-b) + 32px);
  display: flex; flex-direction: column; justify-content: center;
}
.gate-mark {
  font-family: var(--serif); font-size: 44px; letter-spacing: -.03em;
  font-weight: 600; margin: 0; line-height: 1;
}
.gate-mark em { font-style: italic; color: var(--ember); }
.gate-lede {
  font-family: var(--serif); font-size: 17px; line-height: 1.45;
  color: var(--paper-2); font-style: italic;
  margin: 12px 0 30px; max-width: 26ch;
}

.gate .choice { margin: 0 0 20px; }

.gate-form { display: block; }
.gate-field { display: block; margin-bottom: 14px; }
.gate-field .eyebrow { display: block; margin-bottom: 6px; }
.gate-field .field { width: 100%; font-size: 17px; }

.gate-note {
  margin: 2px 0 14px; font-size: 13.5px; line-height: 1.45;
  color: var(--paper-2);
}
.gate-note.bad { color: var(--rose); }

/* Under the box it is about, not at the foot of the form. The rule is shown
   before it is broken; the error replaces nothing and simply joins it. */
.gate-hint {
  margin: 6px 0 0; font-size: 12.5px; line-height: 1.4; color: var(--paper-3);
}
.gate-error {
  margin: 6px 0 0; font-size: 13px; line-height: 1.4; color: var(--rose);
}
.gate-field .field.bad { border-color: var(--rose); }

.gate-go { width: 100%; justify-content: center; padding: 15px 20px; font-size: 15.5px; }
.gate-go:disabled { opacity: .55; }

/* Several short paragraphs in a sheet, for the two or three places where
   the app owes somebody an explanation rather than a label. */
.prose { margin: 0 0 16px; }
.prose p {
  margin: 0 0 12px; font-size: 14px; line-height: 1.55; color: var(--paper-2);
}
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--paper); font-weight: 620; }
.prose .warn { color: var(--rose); }

/* Quiet on purpose. They have to be there and they have to be findable;
   they do not have to compete with anything. */
.legal-links {
  margin: 16px 0 2px; font-size: 12.5px; color: var(--paper-4);
  display: flex; gap: 8px; justify-content: center;
}
.legal-links a { color: var(--paper-3); text-decoration: none; border-bottom: 1px solid var(--rule); }
.legal-links a:active { color: var(--paper); }

/* ---------- the bar ----------
   Three screens, one bar, always there and always the same. It sits outside
   the pager so it neither scrolls nor slides with the panes. */

#navbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 60;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  height: var(--bar-total); padding-bottom: var(--safe-b);
  display: flex; align-items: stretch;
  background: rgba(20,15,12,.86);
  -webkit-backdrop-filter: blur(22px) saturate(1.3); backdrop-filter: blur(22px) saturate(1.3);
  border-top: 1px solid var(--rule);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #navbar { background: #1a1310; }
}
.nav-btn {
  flex: 1; display: grid; place-items: center;
  color: var(--paper-4);
  transition: color .18s var(--ease);
}
.nav-btn svg { width: 23px; height: 23px; }
.nav-btn[aria-current="true"] { color: var(--paper); }
.nav-btn:active { transform: scale(.94); }
/* The camera is the middle button and the app's home; lit when you are on it. */
.nav-btn[data-pane="camera"][aria-current="true"] { color: var(--ember); }

/* ---------- empty states ---------- */

.empty { padding: 54px 20px 30px; max-width: 34ch; }
.empty h2 {
  font-family: var(--serif); font-size: 27px; line-height: 1.14;
  letter-spacing: -.02em; font-weight: 600; margin: 0 0 12px;
}
.empty p {
  font-family: var(--serif); font-size: 17px; line-height: 1.5;
  color: var(--paper-2); margin: 0 0 20px;
}
.empty .hint { font-family: var(--sans); font-size: 13px; color: var(--paper-3); font-style: normal; }

.bigbtn {
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--ember); color: #1a0e07;
  font-weight: 650; font-size: 14.5px; letter-spacing: .01em;
  padding: 13px 20px; border-radius: 3px;
}
.bigbtn.ghost { background: none; color: var(--paper); border: 1px solid var(--rule); }
/* The one button in the app that cannot be taken back. It should not look
   like the button that takes the next photograph. */
.bigbtn.danger { background: var(--rose); color: #1a0709; }
.bigbtn:disabled { opacity: .45; }
.bigbtn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

/* ---------- search ---------- */

.searchbar {
  display: flex; align-items: center; gap: 12px; width: 100%;
  border-bottom: 1px solid var(--rule); padding: 0 0 10px;
}
.searchbar svg { width: 19px; height: 19px; fill: none; stroke: var(--paper-3); stroke-width: 1.8; flex: none; }
.searchbar input {
  flex: 1; background: none; border: 0; outline: none; min-width: 0;
  font-family: var(--serif); font-size: 22px; letter-spacing: -.015em;
  color: var(--paper); padding: 4px 0;
}
.searchbar input::placeholder { color: var(--paper-4); font-style: italic; }

.res-group { padding: 26px 0 0; }
.res-group > .eyebrow { padding: 0 20px 10px; display: block; }
.res-row {
  display: block; width: 100%; text-align: left;
  padding: 14px 20px; border-bottom: 1px solid var(--rule-soft);
}
.res-row:hover { background: var(--ink-1); }
.res-row .t { font-family: var(--serif); font-size: 18px; line-height: 1.35; letter-spacing: -.012em; }
.res-row .s { margin-top: 5px; }
.res-row mark { background: rgba(239,125,78,.22); color: var(--ember-2); border-radius: 2px; padding: 0 1px; }
/* ---------- sheet ---------- */

#layer:empty { display: none; }
/* The overlays live in #layer, outside the pager, so each one is told about
   the column itself. A sheet rising from the bottom of the phone, dimming
   the phone — not the desk it is sitting on.

   ---------------- the ladder, in one place ----------------

   Everything fixed and full-screen shares #layer, so these numbers are the
   only thing deciding what you can actually touch:

      30  the topbar, sticky inside a pane
      60  the bar at the bottom
      70  the progress line, and a profile (the bar stays on it)
      90  a thread, and the photo viewer — screens you enter and back out of
      95  a SHEET, above any screen that could have raised one
     100  toasts, so a failure is readable over whatever caused it
     110  the login gate, which is the only thing on screen when it is up

   The sheet was 80, below the thread and the viewer at 90 — and it mounts
   into the same container as both. A picker opened from inside a
   conversation was therefore built correctly and rendered underneath it:
   the plus appeared to do nothing, and the sheet turned up on the screen
   behind once you had left. Anything modal has to sit above the screen it
   was raised from, and a sheet is always modal. */
.scrim {
  position: fixed; inset: 0; z-index: 95;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  background: rgba(8,5,4,.66);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  animation: fade .22s var(--ease);
  display: flex; align-items: flex-end; justify-content: center;
}
@keyframes fade { from { opacity: 0 } }
@keyframes rise { from { transform: translateY(26px); opacity: .4 } }

.sheet {
  width: 100%; max-width: var(--app-w);
  background: var(--ink-1);
  border: 1px solid var(--rule); border-bottom: 0;
  border-radius: 16px 16px 0 0;
  padding: 10px 20px calc(20px + var(--safe-b));
  animation: rise .26s var(--ease);
  max-height: 88dvh; overflow-y: auto;
  box-shadow: 0 -20px 60px rgba(0,0,0,.5);
}
.grab { width: 34px; height: 4px; border-radius: 2px; background: var(--rule); margin: 0 auto 16px; }
.sheet h2 {
  font-family: var(--serif); font-size: 24px; letter-spacing: -.02em;
  font-weight: 600; margin: 0 0 4px;
}
.sheet .sub { font-size: 13.5px; color: var(--paper-3); margin: 0 0 18px; }

.field {
  width: 100%; background: var(--ink-0); border: 1px solid var(--rule);
  border-radius: 3px; padding: 13px 14px; outline: none;
  font-family: var(--serif); font-size: 19px; letter-spacing: -.01em;
  color: var(--paper); resize: none;
}
.field::placeholder { color: var(--paper-4); font-style: italic; }
.field:focus { border-color: var(--ember); }
textarea.field { min-height: 170px; line-height: 1.5; font-size: 18px; }

.sheet-actions { display: flex; gap: 10px; align-items: center; margin-top: 16px; }
.sheet-actions .grow { flex: 1; }

/* Just me, or with people. */
.choice { display: flex; gap: 8px; margin: 12px 0 4px; }
.choice button {
  flex: 1; text-align: center; padding: 11px 0; border-radius: 8px;
  border: 1px solid var(--rule); color: var(--paper-2); font-size: 14px;
}
.choice button.on {
  border-color: var(--accent); color: var(--paper);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* A friend you tick into a new album. */
.who {
  display: flex; align-items: center; gap: 11px; width: 100%;
  text-align: left; padding: 9px 2px;
  border-bottom: 1px solid var(--rule-soft);
}
.who:last-child { border-bottom: 0; }
.who .avatar { width: 32px; height: 32px; font-size: 15px; }
/* A chosen picture fills the circle the initial was sitting in. */
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.who-name {
  flex: 1; min-width: 0; font-family: var(--serif); font-size: 16px;
  letter-spacing: -.012em; color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.who .check {
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--rule); color: transparent;
}
.who .check svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.who.on .check { border-color: var(--accent); color: var(--accent); }

.pick { display: block; width: 100%; text-align: left; padding: 15px 4px; border-bottom: 1px solid var(--rule-soft); position: relative; }
.pick:last-of-type { border-bottom: 0; }
.pick .n { font-family: var(--serif); font-size: 19px; letter-spacing: -.015em; }
.pick .m { margin-top: 3px; }
.pick.sel .n { color: var(--accent); }

/* The close sheet numbers its options.
   The number is not decoration: it says at a glance how short the list is,
   which is the promise the sheet is making — one tap and the album closes. */
.pick .num {
  position: absolute; left: 2px; top: 17px;
  width: 18px; text-align: left;
  font-size: 13px; color: var(--paper-4); line-height: 1;
}
.pick:has(.num) { padding-left: 26px; }

/* Sending from your own albums, which is the only way anything gets into a
   chat. Square tiles, same three columns as an album's own grid, so what you
   are picking from looks like where it came from. */
.sendgrid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 3px; margin-top: 12px;
}
.sendtile {
  position: relative; aspect-ratio: 1; overflow: hidden;
  background: var(--ink-1); display: block;
}
.sendtile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sendtile:active { opacity: .7; }

/* Picked. A tick AND a border AND a shrink: on a grid of photographs a
   single cue is easy to miss, and the whole point of picking several is
   being able to see at a glance what is going. */
.sendtile-tick {
  position: absolute; top: 5px; right: 5px; z-index: 2;
  width: 19px; height: 19px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(16,12,10,.55); border: 1px solid rgba(243,235,225,.35);
  opacity: 0; transform: scale(.7);
  transition: opacity .14s var(--ease), transform .14s var(--ease);
}
.sendtile-tick svg {
  width: 12px; height: 12px; fill: none; stroke: var(--paper);
  stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
}
.sendtile.sel .sendtile-tick {
  opacity: 1; transform: scale(1);
  background: var(--accent); border-color: var(--accent);
}
.sendtile.sel .sendtile-tick svg { stroke: #1a0e07; }
.sendtile.sel { outline: 2px solid var(--accent); outline-offset: -2px; }
.sendtile.sel img { transform: scale(.92); transition: transform .14s var(--ease); }

/* Back to the album list, above the album's own row. */
.sheet-back {
  display: flex; align-items: center; gap: 2px;
  margin: 0 0 4px -6px; padding: 6px;
  color: var(--paper-3); font-size: 13px;
}
.sheet-back svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* The send arrow, once anything is picked. It is the only way things leave
   this sheet, so it is deliberately hard to miss and sits where a thumb is. */
.sendbar { display: flex; justify-content: flex-end; padding-top: 12px; }
.sendbar-go {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 18px 11px 15px; border-radius: 999px;
  background: var(--accent); color: #1a0e07;
  font-family: var(--serif); font-size: 15.5px;
  animation: fade .16s var(--ease);
}
.sendbar-go svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  transform: rotate(90deg);
}
.sendbar-n {
  min-width: 21px; height: 21px; border-radius: 999px;
  display: grid; place-items: center;
  background: rgba(26,14,7,.22); font-size: 12.5px; padding: 0 5px;
}
.sendbar-go:active { transform: scale(.97); }

/* An album cannot go with items, and says so rather than failing later.
   Its reason goes on its own line: run inline after a struck-out title it
   wrapped into a paragraph and read as damage rather than as an
   explanation. No strikethrough either — that says "deleted", not
   "not right now". */
.pick:disabled { opacity: .45; }
.pick[data-act="send-album"] .m { display: block; margin-top: 3px; }
/* An album sent in a chat: a card, not a picture. It is a reference to
   something still running, so it cannot be a thumbnail of a fixed moment. */
.msg-album {
  display: grid; grid-template-columns: auto 1fr; gap: 3px 8px;
  align-items: center; text-align: left;
  padding: 10px 12px; border-radius: 12px;
  border: 1px solid var(--accent); background: var(--ink-1);
  max-width: 240px;
}
.msg-album-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent); grid-row: span 2;
}
.msg-album-name {
  font-family: var(--serif); font-size: 15.5px; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-album-meta { grid-column: 2; font-size: 10px; }

.pick:hover { background: var(--ink-2); }

.menu-item {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  padding: 15px 4px; border-bottom: 1px solid var(--rule-soft);
  font-size: 15.5px; color: var(--paper);
}
.menu-item:last-child { border-bottom: 0; }
.menu-item svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; flex: none; color: var(--paper-3); }
.menu-item:hover { background: var(--ink-2); }
.menu-item.danger { color: var(--rose); }
.menu-item.danger svg { color: var(--rose); }

/* ---------- viewer ---------- */

.viewer {
  position: fixed; inset: 0; z-index: 90; background: #0a0705;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  display: flex; flex-direction: column; animation: fade .2s var(--ease);
}
/* Four things, and nothing else: the way out, who took it, a way to send it
   on, and when. No counter, because there is nothing to count. */
.viewer-out {
  position: absolute; z-index: 2;
  top: calc(var(--safe-t) + 10px); left: 8px;
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: var(--paper);
}
.viewer-out svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 1px 5px rgba(0,0,0,.7)); }

.viewer-rail {
  position: absolute; z-index: 2; right: 12px;
  bottom: calc(var(--safe-b) + 30px);
  display: flex; flex-direction: column; align-items: center; gap: 18px;
}
.viewer-who {
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(20,15,12,.55); border: 1.5px solid rgba(243,235,225,.7);
  font-family: var(--serif); font-size: 18px; color: var(--paper);
}
.viewer-share {
  width: 42px; height: 42px; display: grid; place-items: center;
  color: var(--paper);
}
.viewer-share svg { width: 25px; height: 25px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 1px 5px rgba(0,0,0,.7)); }
.viewer-share:active { transform: scale(.9); }

.viewer-when {
  position: absolute; z-index: 2; left: 16px;
  bottom: calc(var(--safe-b) + 18px);
  color: var(--paper-2); text-shadow: 0 1px 5px rgba(0,0,0,.8);
}
.viewer-stage { flex: 1; display: grid; place-items: center; min-height: 0; padding: 0; }
.viewer-stage img, .viewer-stage video { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Room at both ends for the chrome that floats over it, and its own
   scrolling: a note longer than the screen has to be readable, which is why
   a vertical swipe only turns into navigation once this has nothing left to
   scroll in that direction. */
.viewer-msg {
  padding: calc(var(--safe-t) + 76px) 24px calc(var(--safe-b) + 96px);
  overflow-y: auto; overscroll-behavior: contain; align-self: stretch; width: 100%;
  font-family: var(--serif); font-size: 21px; line-height: 1.58; white-space: pre-wrap;
  overflow-wrap: anywhere; max-width: 44ch; margin: 0 auto;
}
/* Enough shade top and bottom that white controls stay readable over a
   bright photo, without a bar. */
.viewer::before, .viewer::after {
  content: ""; position: absolute; left: 0; right: 0; height: 150px;
  pointer-events: none; z-index: 1;
}
.viewer::before { top: 0; background: linear-gradient(rgba(10,7,5,.6), rgba(10,7,5,0)); }
.viewer::after { bottom: 0; background: linear-gradient(rgba(10,7,5,0), rgba(10,7,5,.6)); }

/* ---------- toasts ---------- */

#toasts {
  position: fixed; z-index: 100; left: 0; right: 0;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  bottom: calc(var(--bar-total) + var(--bottom-furniture) + 12px);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none; padding: 0 16px;
}
.toast {
  background: #2b211b; border: 1px solid var(--rule);
  color: var(--paper); border-radius: 3px; padding: 11px 15px;
  font-size: 13.5px; max-width: 420px; line-height: 1.4;
  box-shadow: 0 12px 34px rgba(0,0,0,.5);
  animation: rise .22s var(--ease); pointer-events: auto;
}
.toast.bad { border-color: #6b3730; background: #35201c; }
.toast strong { display: block; font-size: 13.5px; margin-bottom: 2px; }
.toast span { color: var(--paper-2); }

/* ---------- fatal ---------- */

.fatal { padding: 16vh 24px 40px; max-width: 40ch; margin: 0 auto; }
.fatal h1 {
  font-family: var(--serif); font-size: 32px; line-height: 1.1;
  letter-spacing: -.025em; margin: 0 0 14px;
}
.fatal p { font-family: var(--serif); font-size: 17px; line-height: 1.5; color: var(--paper-2); margin: 0 0 14px; }
.fatal ul { font-size: 14.5px; color: var(--paper-3); line-height: 1.65; padding-left: 18px; margin: 0 0 22px; }

/* scaleX, not width: a percentage width would resolve against the window,
   which is wider than the app. */
.progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 70;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  background: var(--ember);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .3s var(--ease), opacity .3s;
}

/* Wider screens are handled by --app-w on the shell, above. There is
   deliberately no breakpoint here.

   What used to be here was one, and it was broken: it sat before the camera
   section, so the base rules further down the file won on left/right but not
   on transform, and the camera bar and the capture controls were left
   translated 320px off the left edge of the screen. Constraining the shell
   removes the whole class of bug — nothing downstream can half-override it. */


/* ============================================================
   The camera screen
   ============================================================ */

/* Transparent so the fixed preview behind it shows through. */
#pane-camera { background: transparent; }

/* Off-screen but still playing: display:none would suspend it. */
/* Behind the pager and exactly as wide as it. object-fit does the cover
   crop and the transform does the mirror, both on the compositor. */
#cam-video {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  height: 100%;
  object-fit: cover;
  background: #000;
  pointer-events: none;
}

/* The only mirror in the app. A front camera behaves like a mirror because
   that is what people expect to be looking at. It is a property of this
   element and nothing else, so nothing that gets saved can inherit it. */
#cam-video.mirrored { transform: scaleX(-1); }

/* Behind the pager and exactly as wide as it. The drawing code reads this
   box rather than the window, so the preview is never stretched to fill a
   desktop it does not own. */
/* The preview is the background of the whole app: full bleed, behind the
   bar and everything else.

   height is stated rather than derived. A canvas is a replaced element, so
   with height:auto it takes its size from the intrinsic ratio of its width
   and height attributes - 300x150 until something sets them - and the
   bottom inset is dropped as over-constrained. That is what left the picture
   filling a 188px strip at the top of an 812px screen, and the draw loop
   then sized the backing store to match, so it stayed wrong. */
body.cam-blocked #cam-video { opacity: 0; }
body.cam-blocked .cam-rail { display: none; }

/* keeps the controls legible over a bright scene */
.cam-shade {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(
    rgba(0,0,0,.58), rgba(0,0,0,0) 20%,
    rgba(0,0,0,0) 58%, rgba(0,0,0,.66));
}

/* Down the right side, from the top: flip, light, timer. Deliberately not
   hugging the corner - the top of the viewfinder is left empty. */
.cam-rail {
  position: absolute; right: 14px; z-index: 3;
  top: calc(var(--safe-t) + 68px);
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.cam-railbtn {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  display: grid; place-items: center; color: var(--paper);
  background: rgba(20,15,12,.44);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border: 1px solid rgba(243,235,225,.15);
}
.cam-railbtn svg { width: 21px; height: 21px; }
.cam-railbtn:active { transform: scale(.92); }
/* Dimmed, but still takes the tap: pressing it says why it cannot do the
   thing, rather than being a button that silently does nothing. */
.cam-railbtn.unavailable { opacity: .32; }
.cam-railbtn[aria-pressed="true"] { color: var(--gold); border-color: rgba(227,178,95,.55); }
.cam-timer-on { font-size: 13px; font-weight: 700; letter-spacing: -.02em; color: var(--gold); }
#cam-timer.set { border-color: rgba(227,178,95,.55); }

/* Where the album swipe can start.
 *
 * Invisible, and nothing is drawn by it. It exists because the pill is only
 * as wide as the album's name: an album called "1" is a target about a
 * centimetre across, and a swipe has to begin somewhere.
 *
 * The numbers, all measured from --cam-row-b (20px):
 *
 *   shutter      20 → 98    the band stops 4px above it
 *   last photo   34 → 84
 *   the pill    112 → ~144
 *   this band   102 → 154   52px tall, 10px above and below the pill
 *
 * Short on purpose. This strip used to belong to the pane swipe, so a
 * horizontal drag across it no longer moves between chat, camera and
 * albums — everywhere else on the camera screen it still does, which is
 * why the band is 52px rather than the whole lower third.
 *
 * z-index 2 keeps it BENEATH the pill, the rail and the shutter (all 3), so
 * it can never take a tap meant for a control. It is a div rather than a
 * button so double-tap-to-flip still works through it. */
.cam-swipeband {
  position: absolute; z-index: 2;
  left: 16px; right: 16px;
  bottom: calc(var(--cam-row-b) + 82px);
  height: 52px;
  touch-action: none;
}

/* The album everything is landing in, directly above the circle. */
.cam-chip {
  position: absolute; z-index: 3;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--cam-row-b) + 92px);
  max-width: calc(100% - 40px);
  display: flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 999px;
  background: rgba(16,12,10,.5);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border: 1px solid var(--accent);
  /* The pill handles its own horizontal swipe, so the browser must not
     treat one as a scroll or a back-gesture before the app sees it. */
  touch-action: none;
}
.cam-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent); animation: pulse 2.6s var(--ease) infinite;
}
.cam-chip.idle { border-color: rgba(243,235,225,.18); }
.cam-chip.idle .cam-dot { background: var(--paper-4); animation: none; }
.cam-chip-name {
  display: block; font-family: var(--serif); font-size: 14.5px;
  letter-spacing: -.01em; color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cam-chip.idle .cam-chip-name { color: var(--paper-2); font-style: italic; }
#pane-camera.recording .cam-chip,
#pane-camera.recording .cam-rail { opacity: 0; pointer-events: none; }

/* ---------- capture controls ---------- */

.cam-controls {
  position: absolute; left: 0; right: 0; z-index: 3;
  bottom: var(--cam-row-b);
  display: flex; align-items: center; justify-content: center;
}

.shutter {
  position: relative;
  width: 78px; height: 78px; border-radius: 50%; flex: none;
  border: 3px solid var(--paper); background: rgba(243,235,225,.14);
  display: grid; place-items: center;
  transition: border-color .2s var(--ease);
  touch-action: none;   /* a hold belongs to the shutter, not to the pager */
}
.shutter .ring {
  width: 60px; height: 60px; border-radius: 50%; background: var(--paper);
  transition: transform .16s var(--ease), background .2s var(--ease), border-radius .2s var(--ease);
}
.shutter.firing .ring { transform: scale(.8); }

/* Sweeps round the rim while recording, so a hold has a visible length and
   not only a number in the corner. Twelve hours is a long way round. */
.shutter-arc {
  position: absolute; inset: -3px;
  transform: rotate(-90deg); pointer-events: none; opacity: 0;
  transition: opacity .2s var(--ease);
}
.shutter-arc circle {
  fill: none; stroke: var(--rose); stroke-width: 6; stroke-linecap: round;
  stroke-dasharray: 289; stroke-dashoffset: 289;
}
#pane-camera.recording .shutter-arc { opacity: 1; }
#pane-camera.recording .shutter { border-color: var(--rose); }
#pane-camera.recording .shutter .ring {
  background: var(--rose); border-radius: 14px; transform: scale(.5);
}

.cam-rec {
  position: absolute; z-index: 3; top: calc(var(--safe-t) + 74px);
  left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 7px;
  background: rgba(217,112,122,.92); color: #2a0f12;
  padding: 5px 12px; border-radius: 999px;
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.rec-dot { width: 7px; height: 7px; border-radius: 50%; background: #2a0f12; animation: blink 1s steps(2) infinite; }
@keyframes blink { 50% { opacity: .2; } }

/* The self-timer, counting down over the whole viewfinder. */
.cam-count {
  position: absolute; inset: 0; z-index: 4;
  display: grid; place-items: center; pointer-events: none;
  font-family: var(--serif); font-size: 96px; font-weight: 600;
  color: var(--paper); text-shadow: 0 2px 24px rgba(0,0,0,.65);
  font-variant-numeric: tabular-nums;
}
.cam-count span { display: block; animation: countpop .9s var(--ease); }
@keyframes countpop { from { transform: scale(1.5); opacity: 0 } 40% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .cam-count span { animation: none; } }

/* The front camera's flash. There is no LED on that side, so the display is
   the light: pure white at full opacity over everything, including the bar,
   for as long as it takes the sensor to expose for it.

   No fade. A flash that ramps up is a flash that is dim when the frame is
   taken. */
/* The ring light.

   A rounded rectangle of clear preview, inset from the edges, with the white
   fading up out of it and going solid by the time it reaches the screen.

   The shape is a hard-edged hole that is then blurred, rather than a shadow
   cast outward from the opening. Direction is the reason: an outer
   box-shadow is at full strength against the element and fades away from it,
   which is backwards - it would put the white against the opening and let it
   die before the screen edge. Blurring a hole runs the other way, clear in
   the middle and solid at the rim, and it keeps the rounded corners honestly
   round instead of approximating them.

   Brightness is the count of fully opaque pixels, so the fade is kept as
   short as the look allows and everything behind it is flat #fff. The blur
   is what costs light: it spreads a transition of twice its radius, and
   every pixel in there is partly transparent. It used to be a little over
   half the band, which left two thirds of the glow semi-transparent. At a
   sixth of the band the soft edge is still soft and roughly three quarters
   of the glow is solid white.

   --ring-band is where the opening sits, so it is also how deep the glow is,
   with --ring-band-x doing the same for the left and right. The sides are
   held back a little: a phone is narrow, so an even band eats proportionally
   more of the width than of the height. --ring-fade is the blur radius.
   Solid white runs from a screen edge to (band - fade), the transition to
   (band + fade), and the opening starts there. Widen the fade and the white
   creeps inward; widen a band and the opening shrinks on that axis.

   No z-index anywhere. It sits between the preview and the pager in source
   order, and among positioned elements at the same level that is what decides
   the painting order: over the picture, under every control. It reaches all
   four edges of the screen rather than stopping at the pager, which ends
   above the bar. */
.ring-light {
  --ring-band: calc(var(--col) * .165);     /* top and bottom */
  --ring-band-x: calc(var(--col) * .135);   /* sides, held back a little */
  --ring-fade: calc(var(--col) * .027);     /* only the inner edge is soft */
  --ring-radius: 45px;
  position: fixed; inset: 0;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  pointer-events: none;
  /* Keeps the glow inside the app column on a wide screen, where the white
     would otherwise spill into the surround. */
  overflow: hidden;
}

.ring-light::before {
  content: "";
  position: absolute;
  inset: var(--ring-band) var(--ring-band-x);
  border-radius: var(--ring-radius);
  /* Opaque white, everywhere outside the opening. The spread only has to
     clear the screen; it is not 100vmax because the filter below has to
     rasterise whatever this covers. */
  box-shadow: 0 0 0 260px #fff;
  /* The only thing in the whole layer that is not fully opaque, and it is
     confined to the inner edge. */
  filter: blur(var(--ring-fade));
}

/* With the ring up, the shade that keeps the controls legible over a bright
   scene is fighting the light rather than helping it. */
#pane-camera.ringlit .cam-shade { opacity: 0; }

/* The glow runs the full height of the screen, so the bar is sitting on top
   of the brightest part of it. While the light is on the bar stops painting
   itself and lets it through, and its icons go black to stay readable.

   Losing the which-screen-am-I-on colour costs nothing here: the ring only
   shows on the camera screen, so that is the only place this applies. */
/* The pager carries a dark ring and a wide dark glow to make it read as a
   phone-width column on a big screen. Its bottom edge lands exactly where
   the bar starts, so with the glow lit that shadow draws a grey seam across
   the white. The ring is the column edge while it is on, so the shadow has
   nothing left to do. */
body.ringlit #pager { box-shadow: none; }

body.ringlit #navbar {
  background: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border-top-color: transparent;
}
body.ringlit .nav-btn,
body.ringlit .nav-btn[aria-current="true"],
body.ringlit .nav-btn[data-pane="camera"][aria-current="true"] {
  color: #000;
}

/* The two white controls sit in the brightest part of their own light. An
   outline is enough to bring them back without repainting them dark. */
#pane-camera.ringlit .shutter {
  box-shadow: 0 0 0 2px rgba(20,15,12,.5), 0 4px 18px rgba(20,15,12,.3);
}
#pane-camera.ringlit .cam-last {
  box-shadow: 0 0 0 2px rgba(20,15,12,.45), 0 4px 16px rgba(20,15,12,.35);
}

.cam-flash {
  position: absolute; inset: 0; z-index: 4; background: #fff;
  pointer-events: none; animation: flash .26s ease-out forwards;
}
@keyframes flash { from { opacity: .85; } to { opacity: 0; } }

/* ---------- no camera to show ---------- */

/* Below the top bar and the capture controls, not above them. With no live
   preview the controls stay reachable, so the screen never looks dead. */
.cam-block {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-items: center;
  padding: calc(var(--safe-t) + 74px) 24px calc(var(--safe-b) + 150px);
  background: var(--ink-0);
  overflow-y: auto;
}
.cam-block-inner { max-width: 34ch; }
.cam-block h2 {
  font-family: var(--serif); font-size: 25px; line-height: 1.16;
  letter-spacing: -.02em; font-weight: 600; margin: 0 0 12px;
}
.cam-block p {
  font-family: var(--serif); font-size: 16.5px; line-height: 1.5;
  color: var(--paper-2); margin: 0 0 20px;
}
.cam-block .hint {
  font-family: var(--sans); font-size: 12.5px; color: var(--paper-3);
  margin: 18px 0 0;
}
.cam-block-actions { display: flex; gap: 10px; flex-wrap: wrap; }


/* ============================================================
   People
   ============================================================ */

.you { padding: 22px 20px 4px; }
.you > .eyebrow { display: block; margin-bottom: 10px; }
.you-row { display: flex; align-items: baseline; gap: 14px; }
.you-name {
  font-family: var(--serif); font-size: 24px; letter-spacing: -.02em;
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.you-hint {
  font-size: 12.5px; line-height: 1.5; color: var(--paper-3);
  margin: 8px 0 0; max-width: 44ch;
}

/* The find-someone row. It lives in a sheet now, which supplies its own
   side padding, so this no longer adds any. */
.add-row { display: flex; gap: 8px; }
.add-row .field { flex: 1; min-width: 0; font-size: 16px; padding: 11px 13px; }
.add-row .bigbtn { padding: 11px 16px; flex: none; }
#find-result:not(:empty) { margin-top: 10px; }

.person {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 20px; border-bottom: 1px solid var(--rule-soft);
}
.person:last-child { border-bottom: 0; }
.avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: grid; place-items: center; overflow: hidden;
  background: var(--ink-3); border: 1px solid var(--rule);
  font-family: var(--serif); font-size: 18px; color: var(--paper-2);
}
.person-text { flex: 1; min-width: 0; }
/* Your own picture, top left of the chat screen. */
.avatar-btn { cursor: pointer; }

.avatar-btn:hover { border-color: var(--paper-4); color: var(--paper); }

/* Pinned to the top of the chat list. Quiet: it marks a row that is already
   in an unusual place, so it only has to explain why — not compete with the
   name it sits beside. */
.crow.pinned { background: linear-gradient(90deg, var(--ink-1), transparent 60%); }
.crow-pin {
  display: inline-flex; vertical-align: middle;
  width: 13px; height: 13px; margin-left: 7px;
  color: var(--accent);
}
.crow-pin svg {
  width: 13px; height: 13px; fill: none; stroke: currentColor;
  stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
}
.pick.danger .n { color: var(--rose); }
.person-name {
  display: block; font-family: var(--serif); font-size: 18px;
  letter-spacing: -.012em; color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.person-sub {
  display: block; font-size: 11.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--paper-3); margin-top: 2px;
}
.person-sub:empty { display: none; }
.person-actions { display: flex; gap: 7px; align-items: center; flex: none; }

.pill {
  font-size: 12px; font-weight: 650; letter-spacing: .02em;
  padding: 8px 13px; border-radius: 999px;
  background: var(--ember); color: #1a0e07;
}
.pill.ghost { background: none; color: var(--paper-2); border: 1px solid var(--rule); }
.pill.ghost:hover { color: var(--paper); border-color: var(--paper-4); }
.pill:disabled { opacity: .5; }

.person-empty {
  padding: 14px 20px; margin: 0;
  font-family: var(--serif); font-size: 16px; color: var(--paper-3);
  line-height: 1.5;
}


/* Proof the shutter did something, and a way through to the album.

   Back on the shutter’s own row, bottom left. It can sit there again
   because that row is now a single centred circle: the Note and Video
   buttons that used to flank it — and that it collided with on a 375px
   phone — are gone. Press is a photo, hold is a video. */
.cam-last {
  position: absolute; left: 18px; z-index: 3;
  bottom: calc(var(--cam-row-b) + 14px);
  width: 46px; height: 46px; border-radius: 6px; overflow: hidden;
  border: 2px solid rgba(243,235,225,.85);
  box-shadow: 0 4px 16px rgba(0,0,0,.5);
  background: var(--ink-2);
  animation: rise .3s var(--ease);
}
.cam-last img { width: 100%; height: 100%; object-fit: cover; }

/* One muted line on failure screens saying exactly why the camera did not
   start. Not debug scaffolding — it is the difference between "it doesn't
   work" and a report someone can act on. */
.cam-block .diag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; color: var(--paper-4); margin-top: 6px;
}


/* who took it, in a shared album */
.tl-by {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 20px 0;
}
.tl-by .meta { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-x {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font-size: 17px; line-height: 1; color: var(--paper-3);
  border: 1px solid var(--rule);
}
.tl-x:hover { color: var(--paper); border-color: var(--paper-4); }
.tl-private {
  padding: 7px 20px 0; color: var(--paper-4);
}


/* an invitation you never see is one that was never sent */
/* An invitation is answered where it stands, so the band is built from the
   same .crow row as a friend request rather than its own card. The album's
   name takes the serif, because here the name is the subject — on the chat
   list that slot holds a person's name and stays in the sans. */
.invites { padding: 4px 20px 18px; border-bottom: 1px solid var(--rule-soft); margin-bottom: 22px; }
.invites .eyebrow { display: block; margin-bottom: 4px; }
.invites .crow-name {
  font-family: var(--serif); font-size: 18px;
  letter-spacing: -.015em; color: var(--paper);
}


/* ============================================================
   Messages and profiles
   ============================================================ */

/* ---------- the chat list ---------- */

/* Two tap targets per row, side by side. The circle is a button and the
   rest of the row is a button, so there is no row underneath the circle for
   a tap to fall through to — and each one gets its own name read out. */
.crow {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 20px;
  border-bottom: 1px solid var(--rule-soft);
}
.crow:last-child { border-bottom: 0; }

.crow-av {
  /* 44 is the smallest thing a thumb hits reliably. The circle is drawn at
     that size rather than padded out to it, so what you aim at is what you
     hit. */
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  font-family: var(--serif); font-size: 17px; line-height: 1;
  color: var(--paper-2);
  background: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--rule);
}
.crow-av:hover { color: var(--paper); box-shadow: inset 0 0 0 1px var(--paper-4); }
.crow-av:active { transform: scale(.94); }

.crow-main {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
  text-align: left; padding: 6px 0;
}
.crow-name {
  font-size: 15px; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crow-last {
  font-size: 12.5px; color: var(--paper-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crow-actions { display: flex; gap: 7px; align-items: center; flex: none; }
.crow-ask .crow-last { color: var(--ember); }

/* ---------- a conversation ---------- */

/* Over the bar, not above it: this is a screen you enter and back out of,
   so the three you swipe between are not offered while you are in it. */
.thread {
  position: fixed; inset: 0; z-index: 90;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  background: var(--ink-0);
  display: flex; flex-direction: column;
  animation: fade .2s var(--ease);
}
.thread-top {
  display: flex; align-items: center; gap: 6px;
  padding: calc(var(--safe-t) + 10px) 12px 10px;
  border-bottom: 1px solid var(--rule-soft);
  background: rgba(16,12,10,.9);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
}
/* Their picture and their name are one target. Two would mean one of them
   could be missed, and both do the same thing. */
.thread-who {
  display: flex; align-items: center; gap: 10px;
  flex: 1; min-width: 0; padding: 4px 6px 4px 0;
}
.thread-who .avatar { width: 30px; height: 30px; font-size: 14px; }
.thread-name {
  font-size: 15.5px; color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.thread-body {
  flex: 1; min-height: 0;
  overflow-y: auto; overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: 7px;
  padding: 14px 12px 18px;
  scrollbar-width: none;
}
.thread-body::-webkit-scrollbar { display: none; }
.thread-day { align-self: center; padding: 8px 0 4px; }
.thread-empty { color: var(--paper-4); text-align: center; padding: 40px 20px; font-style: italic; }

.msg {
  align-self: flex-start; max-width: 74%;
  padding: 7px 12px 6px;
  border-radius: 15px 15px 15px 5px;
  background: var(--ink-3);
  font-size: 14.5px; line-height: 1.4;
  overflow-wrap: anywhere;
}
.msg.mine {
  align-self: flex-end;
  background: var(--ember); color: #1b1206;
  border-radius: 15px 15px 5px 15px;
}
.msg-who {
  display: block; font-size: 11px; font-weight: 600;
  color: var(--gold); margin-bottom: 2px;
}
.msg-body { white-space: pre-wrap; }
.msg-when {
  display: block; text-align: right;
  font-size: 10px; margin-top: 2px; opacity: .62;
}
.msg.mine .msg-when { color: #1b1206; }
.msg.pending { opacity: .6; }
.msg.failed { background: var(--rose); color: #1a0e07; }

/* A picture in the flow is the picture, not a bubble around one. */
.msg-media { padding: 3px; background: var(--ink-2); }
.msg.mine.msg-media { background: var(--ink-2); }
.msg-tile {
  position: relative; display: block;
  width: 168px; max-width: 100%; aspect-ratio: 4 / 3;
  overflow: hidden; border-radius: 12px; background: var(--ink-3);
}
.msg-tile img { width: 100%; height: 100%; object-fit: cover; }
/* Three classes beat two, so `.msg.mine .msg-when` was painting the time
   under your own photo in the dark ink meant for the accent bubble — black
   on near-black. A media bubble is not accent-coloured whoever sent it. */
.msg-media .msg-when, .msg.mine.msg-media .msg-when {
  color: var(--paper-3); padding: 0 5px 1px; opacity: 1;
}

/* A message taken back. Deliberately plain and quiet — it is a fact about
   the conversation, not a thing to look at. */
.msg-deleted .msg-gone {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: 14px;
  border: 1px dashed var(--rule); color: var(--paper-4);
  font-size: 13.5px; font-style: italic;
}
.msg-deleted .msg-gone svg {
  width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

/* Kept, so it will not expire. The mark matters more than it looks: without
   it there is no way to tell what you have saved from what is about to go. */
.msg-keptmark {
  display: inline-flex; align-items: center; gap: 3px;
  margin-top: 3px; font-size: 10.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent);
}
.msg-keptmark svg {
  width: 11px; height: 11px; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.msg-expiring .msg-body { cursor: pointer; }

/* Full size over the thread. There is nothing to swipe between here and
   nothing to share on, so it is a picture and a way back. */
.msg-full {
  position: absolute; inset: 0; z-index: 5;
  background: #0a0705;
  display: grid; place-items: center;
  animation: fade .18s var(--ease);
}
.msg-full img, .msg-full video { max-width: 100%; max-height: 100%; object-fit: contain; }

.compose {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px calc(9px + var(--safe-b));
  border-top: 1px solid var(--rule);
  background: var(--ink-1);
}
.compose-field {
  flex: 1; min-width: 0;
  background: var(--ink-3); border: 1px solid var(--rule);
  border-radius: 18px; padding: 9px 14px;
  font-size: 15px; color: var(--paper);
}
.compose-field::placeholder { color: var(--paper-4); }
.compose-field:focus { outline: none; border-color: var(--paper-4); }
.compose-send {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--ember); color: #1b1206;
}
.compose-send svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.compose-send:active { transform: scale(.92); }

/* ---------- a profile ---------- */

/* Stops above the bar rather than covering it: the three screens stay
   reachable from a profile, which is what the design asks for. */
.profile {
  position: fixed; top: 0; left: 0; right: 0; bottom: var(--bar-total);
  z-index: 70;
  width: 100%; max-width: var(--app-w); margin-inline: auto;
  background: var(--ink-0);
  animation: fade .2s var(--ease);
}
.profile-scroll {
  height: 100%; overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain;
  display: flex; flex-direction: column;
  scrollbar-width: none;
}
.profile-scroll::-webkit-scrollbar { display: none; }

.profile-back { margin: calc(var(--safe-t) + 8px) 0 0 8px; }

.profile-head {
  display: flex; align-items: center; gap: 18px;
  padding: 4px 20px 12px;
}
.profile-pic {
  width: 62px; height: 62px; flex: none;
  border-radius: 50%; display: grid; place-items: center; overflow: hidden;
  background: var(--ink-2); box-shadow: inset 0 0 0 1px var(--rule);
  font-family: var(--serif); font-size: 25px; color: var(--paper-2);
}
/* A chosen picture fills the circle the letter was sitting in. */
.profile-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.profile-pic:disabled { cursor: default; }

.profile-stats { display: flex; gap: 26px; flex: 1; }
.stat { display: grid; justify-items: center; gap: 1px; }
.stat-n { font-size: 17px; color: var(--paper); }
.stat-l {
  font-family: var(--sans); font-size: 10px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--paper-3);
}

.profile-desc {
  display: block; width: 100%; text-align: left;
  padding: 0 20px 14px;
  font-size: 12.5px; line-height: 1.45; color: var(--paper-3);
}
.profile-desc:disabled { cursor: default; }

.profile-btns { display: flex; gap: 9px; padding: 0 20px 14px; }
.pbtn {
  flex: 1; text-align: center; padding: 8px 0;
  border: 1px solid var(--rule); border-radius: 9px;
  font-size: 13.5px; color: var(--paper);
}
.pbtn.pri { background: var(--accent); border-color: var(--accent); color: #1b1206; }
.pbtn:active { transform: scale(.98); }

/* Putting an album up. Only on your own profile: nothing appears there
   unless you put it there. */
.profile-add {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 20px 14px; padding: 11px 0; width: calc(100% - 40px);
  border: 1px dashed var(--rule); border-radius: 10px;
  color: var(--paper-3); font-size: 14px;
}
.profile-add svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round;
}
.profile-add:active { background: var(--ink-1); }

/* Left out of the copy that goes up. The album keeps it either way. */
.sendtile.left-out { opacity: .28; }
.sendtile.left-out .sendtile-tick { opacity: 0; }
.sendtile[disabled] { opacity: .45; }

.profile-search { display: block; padding: 0 20px 12px; }
.profile-search input {
  width: 100%;
  background: var(--ink-2); border: 1px solid var(--rule);
  border-radius: 9px; padding: 8px 12px;
  font-size: 13.5px; color: var(--paper);
}
.profile-search input::placeholder { color: var(--paper-4); }
.profile-search input::-webkit-search-cancel-button { -webkit-appearance: none; }
.profile-search input:focus { outline: none; border-color: var(--paper-4); }

/* Edge to edge, three across, filling what is left of the screen. */
.profile-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 2px; padding-bottom: 20px;
}
.profile-grid > .person-empty { grid-column: 1 / -1; }
.ptile {
  position: relative; display: block;
  aspect-ratio: 1; overflow: hidden;
  background: var(--ink-2);
}
.ptile img { width: 100%; height: 100%; object-fit: cover; }
/* An album with no cover is a blank square with nothing to say what it is,
   so that one carries its name. A tile with a picture does not need to. */
.ptile-name {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 8px; text-align: center;
  font-family: var(--serif); font-size: 12.5px; line-height: 1.3;
  color: var(--paper-3);
}
