aboutsummaryrefslogtreecommitdiffstats
path: root/src/style.css
diff options
context:
space:
mode:
Diffstat (limited to 'src/style.css')
-rw-r--r--src/style.css244
1 files changed, 225 insertions, 19 deletions
diff --git a/src/style.css b/src/style.css
index bbaae88..31231fe 100644
--- a/src/style.css
+++ b/src/style.css
@@ -30,6 +30,12 @@
--accent-fg: #ffffff; /* text on accent */
--accent-ink: #000000;
--accent-soft: #ececef;
+ /* a button's fill (primary, chosen chip or tab): the accent in light; in
+ dark a lifted grey, not a white slab (Adam, 2026-10-18: "Probably
+ shouldn't have white buttons in dark mode") */
+ --fill: var(--accent);
+ --fill-hover: var(--accent-hover);
+ --fill-fg: var(--accent-fg);
--green: #14935a;
--green-soft: #e7f6ee;
--green-line: #c4e7d4;
@@ -65,6 +71,9 @@
--accent-fg: #0f1216; /* text on accent */
--accent-ink: #e7e9ee;
--accent-soft: #262b33;
+ --fill: #353b45;
+ --fill-hover: #404753;
+ --fill-fg: #f3f5f8;
--green: #2dbd80;
--green-soft: #14271d;
--green-line: #235340;
@@ -230,8 +239,8 @@ button, .btn {
button:hover { background: var(--surface2); }
button:active { transform: translateY(1px); }
button:disabled { opacity: .5; cursor: not-allowed; }
-.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
-.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
+.btn-primary { background: var(--fill); border-color: var(--fill); color: var(--fill-fg); }
+.btn-primary:hover { background: var(--fill-hover); border-color: var(--fill-hover); }
.btn-ghost { background: transparent; }
.btn-sm { padding: 7px 11px; font-size: 13px; border-radius: 9px; }
.btn-sm.on { border-color: var(--accent); color: var(--accent); }
@@ -296,7 +305,7 @@ textarea.send-dest::placeholder { font-family: var(--sans); }
colour matched the posts under it). So these take the feed chips' look:
the chosen tab solid accent, the other one quiet (Adam, 2026-10-16). */
.prof-tabs button { background: var(--surface2); color: var(--muted); }
-.prof-tabs button.active { background: var(--accent); color: var(--accent-fg); }
+.prof-tabs button.active { background: var(--fill); color: var(--fill-fg); }
/* ---- address / qr ---- */
.qr { width: 220px; height: 220px; display: block; border-radius: 12px; border: 1px solid var(--line); }
@@ -726,7 +735,7 @@ html:has(#app .chat-card), body:has(#app .chat-card) { overflow: hidden; }
.pos-big { font-size: 34px; font-weight: 700; line-height: 1.1; letter-spacing: -.5px; }
.pos-tips { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; width: 100%; }
.pos-tip { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 12px 6px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface2); color: var(--ink); cursor: pointer; }
-.pos-tip.on { border-color: var(--accent); background: var(--accent); color: var(--accent-fg); }
+.pos-tip.on { border-color: var(--fill); background: var(--fill); color: var(--fill-fg); }
.pos-tip.on .muted { color: inherit; opacity: .8; }
.pos-tip-label { font-size: 20px; font-weight: 600; }
.pos-total { font-size: 22px; font-weight: 600; margin-top: 4px; }
@@ -735,14 +744,12 @@ html:has(#app .chat-card), body:has(#app .chat-card) { overflow: hidden; }
.pos-sale.faint { opacity: .55; }
.pos-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; width: 100%; }
.pos-key { padding: 14px 0; font-size: 22px; font-weight: 600; border: 1px solid var(--line); border-radius: 12px; background: var(--surface2); color: var(--ink); cursor: pointer; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
-.pos-key:active { background: var(--accent); color: var(--accent-fg); }
+.pos-key:active { background: var(--fill); color: var(--fill-fg); }
.pos-key.del { font-weight: 400; }
/* the feeds row: pills that scroll sideways, the one on screen filled */
.feed-chips { gap: 6px; flex-wrap: nowrap; overflow-x: auto; margin: 0 -14px; padding: 2px 14px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.feed-chips::-webkit-scrollbar { display: none; }
/* with a mouse: a thin bar says the strip scrolls, and it drags (app.js) */
-/* grabbing the feed to scroll it (app.js) */
-.grab-scrolling, .grab-scrolling * { cursor: grabbing !important; user-select: none !important; }
@media (hover: hover) and (pointer: fine) {
.feed-chips { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; padding-bottom: 6px; cursor: grab; }
.feed-chips::-webkit-scrollbar { display: block; height: 6px; }
@@ -751,7 +758,7 @@ html:has(#app .chat-card), body:has(#app .chat-card) { overflow: hidden; }
.feed-chips.dragging .feed-chip { pointer-events: none; }
}
.feed-chip { flex: 0 0 auto; border: 1px solid var(--line); background: var(--surface2); color: var(--muted); border-radius: 999px; padding: 5px 12px; font-size: 14px; line-height: 1.2; cursor: pointer; white-space: nowrap; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }
-.feed-chip.on { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
+.feed-chip.on { background: var(--fill); color: var(--fill-fg); border-color: var(--fill); }
.feed-chip.add { font-weight: 600; padding: 5px 11px; }
.note-act {
background: none; border: 0; padding: 4px 6px; margin: 0; line-height: 1;
@@ -840,11 +847,29 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
}
.lightbox-x:hover { background: rgba(255, 255, 255, .24); }
@keyframes lightbox-in { from { opacity: 0; } to { opacity: 1; } }
-.note-video { display: block; width: 100%; height: auto; max-height: 100vh; object-fit: contain; border-radius: 10px; margin-top: 6px; background: #000; }
+.note-video { display: block; width: 100%; height: auto; max-height: var(--vid-cap); object-fit: contain; border-radius: 10px; margin-top: 6px; background: #000; }
/* a video plays by itself, muted, while it is on screen; the corner button
turns the sound on (see autoplay in messages.js) */
.note-video-box { position: relative; display: block; max-width: 100%; margin-top: 6px; }
.note-video-box .note-video { margin-top: 0; }
+/* the height a feed clip may take: the whole screen on a phone, three
+ quarters of it on a computer (a portrait clip on a 1920 screen ran off it) */
+:root { --vid-cap: 100vh; }
+@media (min-width: 900px) { :root { --vid-cap: 75vh; } }
+/* with a mouse: the big play/pause in the middle, while the pointer is over the clip */
+.vid-pp {
+ position: absolute; left: 50%; top: 50%; z-index: 2; width: 64px; height: 64px; margin: -32px 0 0 -32px; padding: 0;
+ border: 0; border-radius: 50%; background: rgba(18, 18, 18, .6); color: #fff; cursor: pointer;
+ display: none; align-items: center; justify-content: center;
+}
+.vid-pp:hover { background: rgba(18, 18, 18, .85); }
+.vid-pp-i { width: 0; height: 0; margin-left: 5px; border-style: solid; border-width: 13px 0 13px 22px; border-color: transparent transparent transparent #fff; }
+.vid-pp.playing .vid-pp-i { width: 20px; height: 24px; margin: 0; border-width: 0 7px; border-color: #fff; }
+.has-mouse .note-video-box:hover .vid-pp, .vid-pp:focus-visible { display: flex; }
+/* the moments bar is the clip's only seek bar: the middle button is its play
+ control on a touch screen too, up whenever the clip is paused */
+.note-video-box.own-bar .vid-pp:not(.playing) { display: flex; }
+.note-video-box:has(.vid-heavy) .vid-pp { display: none; }
.vid-sound {
position: absolute; top: 8px; left: 8px; z-index: 2; margin: 0; padding: 4px 10px; border: 0;
border-radius: 999px; background: rgba(18, 18, 18, .7); color: #fff; font-size: 13px; line-height: 1.3;
@@ -879,6 +904,71 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
.addr-cover { width: 100%; aspect-ratio: 2 / 1; object-fit: cover; display: block; background: var(--line); }
.addr-title { font-weight: 650; font-size: 1.05em; line-height: 1.3; }
.addr-summary { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; white-space: normal; }
+/* Live streams (NIP-53): the row of what's on above a feed, a profile's
+ "on now", a post's card, and the stream's own page (player over chat). */
+/* a face whose person is streaming now: a red ring, and LIVE under the bigger ones */
+.chat-avatar.live-ring { box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px #e0245e; position: relative; overflow: visible; }
+.chat-avatar.mini.live-ring { box-shadow: 0 0 0 1px var(--paper), 0 0 0 2.5px #e0245e; }
+.chat-avatar.live-ring:not(.mini)::after { content: 'LIVE'; position: absolute; left: 50%; bottom: -7px; transform: translateX(-50%);
+ padding: 0 4px; border-radius: 3px; font-size: 9px; font-weight: 700; line-height: 13px; letter-spacing: .04em;
+ color: #fff; background: #e0245e; box-shadow: 0 0 0 2px var(--paper); text-transform: none; }
+.live-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 14px 10px; padding: 2px 0 4px; }
+.live-past-title { margin: 6px 0 0; font-size: 15px; }
+.live-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #e0245e; margin-right: 5px; vertical-align: 1px; }
+.live-tile { display: flex; flex-direction: column; gap: 5px; padding: 0; text-align: left;
+ background: none; border: 0; color: inherit; cursor: pointer; min-width: 0; }
+.live-cover { position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden;
+ background: var(--line) center / cover no-repeat; }
+.live-badge { display: inline-block; padding: 1px 6px; border-radius: 4px; font-size: 11px; font-weight: 700;
+ letter-spacing: .04em; color: #fff; background: #555; line-height: 1.5; flex-shrink: 0; }
+.live-badge.live { background: #e0245e; }
+.live-cover .live-badge { position: absolute; top: 6px; left: 6px; }
+.live-viewers { position: absolute; bottom: 6px; left: 6px; padding: 1px 6px; border-radius: 4px; font-size: 11px;
+ color: #fff; background: rgba(0, 0, 0, .6); }
+.live-tile-title { font-weight: 600; font-size: 14px; line-height: 1.25; display: -webkit-box; -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical; overflow: hidden; }
+.live-tile-host { align-items: center; min-width: 0; }
+.live-tile-host .small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.live-banner { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid #e0245e;
+ border-radius: 10px; background: none; color: inherit; cursor: pointer; text-align: left; min-width: 0; }
+.live-banner-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
+.live-card { flex-direction: row; align-items: center; gap: 10px; }
+.live-card .live-cover { flex: 0 0 120px; width: 120px; }
+.live-title { margin: 0; min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.live-player { width: 100%; aspect-ratio: 16 / 9; background: #000; border-radius: 10px; overflow: hidden; }
+.live-frame { width: 100%; height: 100%; }
+.live-video { width: 100%; height: 100%; display: block; background: #000; }
+.live-still { border-radius: 0; height: 100%; display: flex; flex-direction: column; gap: 10px; align-items: center; justify-content: center; }
+.live-rec-link { background: var(--paper); color: var(--ink); text-decoration: none; }
+.live-say { padding: 6px 12px; border-radius: 8px; color: #fff; background: rgba(0, 0, 0, .65); font-size: 14px; text-align: center; }
+/* the top padding is headroom for a hat on the first line's face: a scroller clips what rises out of it */
+.live-chat { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; min-height: 120px; overflow-y: auto; overscroll-behavior: contain; padding-top: 12px; }
+.live-line { display: flex; gap: 8px; align-items: flex-start; min-width: 0; }
+.live-line-body { min-width: 0; overflow-wrap: anywhere; white-space: pre-wrap; line-height: 1.35; }
+.live-zap .live-line-body b { color: var(--accent); }
+.live-chat .chat-row { padding-right: 2px; }
+/* on phones the stream is the page, the way Nostrich lays one out: the
+ picture runs edge to edge and the chat under it is a plain full-width
+ list of lines, no bubbles — the card walls step outside #app's padding */
+@media (max-width: 520px) {
+ .live-page > .card {
+ margin-left: -16px; margin-right: -16px; padding: 10px 0 12px;
+ border-left: none; border-right: none; border-radius: 0; box-shadow: none;
+ }
+ .live-page > .live-chat-card { margin-top: 0; padding-top: 6px; border-top: none; }
+ .live-page > .card > :not(.live-player, .live-chat) { margin-left: 14px; margin-right: 14px; }
+ .live-page .live-player { border-radius: 0; }
+ .live-page .live-chat { padding: 10px 14px 0; max-height: 60vh; }
+ .live-page .live-title { font-size: 17px; }
+ .live-chat .chat-row { gap: 10px; }
+ .live-chat .chat-bubble { display: block; background: transparent; border: 0; border-radius: 0; padding: 0; font-size: 15px; }
+ .live-chat .chat-quote { margin-bottom: 2px; }
+}
+.bc-preview.mirror { transform: scaleX(-1); }
+.bc-bar .bc-clock { font-variant-numeric: tabular-nums; font-weight: 650; }
+.go-live-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #e0245e; box-shadow: 0 0 0 3px rgba(224, 36, 94, .25); }
+.go-live-btn { display: inline-flex; align-items: center; gap: 8px; }
+.go-live-title { width: 100%; }
.article-title { font-size: 1.35em; font-weight: 700; line-height: 1.25; margin-bottom: 6px; white-space: normal; }
.article-h1, .article-h2, .article-h3 { font-weight: 650; line-height: 1.3; margin: 10px 0 2px; white-space: normal; }
.article-h1 { font-size: 1.2em; } .article-h2 { font-size: 1.1em; } .article-h3 { font-size: 1em; }
@@ -954,7 +1044,7 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
position: fixed; bottom: calc(env(safe-area-inset-bottom, 0px) + 18px);
left: 50%; transform: translateX(-50%);
z-index: 30; width: auto; padding: 12px 24px; border-radius: 999px;
- background: var(--accent); color: var(--accent-fg); border: none;
+ background: var(--fill); color: var(--fill-fg); border: none;
font-size: 16px; font-weight: 650; cursor: pointer;
touch-action: none; user-select: none; -webkit-user-select: none; /* it swipes away */
box-shadow: 0 3px 12px rgba(0, 0, 0, .28);
@@ -963,7 +1053,7 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
/* the generic button hover (a light surface) and press (a 1px drop, which
REPLACED the centring transform and threw the pill half its width to the
right, out from under the pointer, so the tap never landed) stay off it */
-.feed-new-pill:hover { background: var(--accent-hover); color: var(--accent-fg); }
+.feed-new-pill:hover { background: var(--fill-hover); color: var(--fill-fg); }
.feed-new-pill:active { transform: translate(-50%, 1px); }
/* the count bumps each time it changes: a fresh node per value replays it */
.feed-new-pill .n { display: inline-block; min-width: 1.1em; text-align: center; animation: pill-bump .32s cubic-bezier(.2,.9,.3,1.4); }
@@ -1189,7 +1279,7 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
.hat-demo { position: relative; display: inline-flex; width: 34px; height: 34px; flex-shrink: 0; margin-top: 12px; }
.hat-demo-head { width: 100%; height: 100%; border-radius: 50%; background: var(--accent-soft); }
.hat-btn { white-space: nowrap; flex-shrink: 0; }
-.hat-btn.wearing { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
+.hat-btn.wearing { background: var(--fill); border-color: var(--fill); color: var(--fill-fg); }
.hat-btn.confirm { border-color: var(--green); color: var(--green); }
/* a small centered confirm popup over a dimmed page */
@@ -1483,6 +1573,20 @@ body::after {
background: var(--surface2); overflow: hidden; cursor: pointer;
}
.media-tile.video { aspect-ratio: 9 / 16; }
+/* only wide clips in it: one to a row, the poster whole */
+.media-grid.wide { grid-template-columns: 1fr; gap: 6px; }
+.media-grid.wide .media-tile.video { aspect-ratio: 16 / 9; }
+/* every shape in it: justified rows. A tile starts at its proportions at
+ the row height and grows in proportion to them, so a row's tiles share one
+ height and fill the width; the filler takes the last row's slack instead
+ of stretching its few tiles. */
+.media-grid.justified { display: flex; flex-wrap: wrap; --row-h: 140px; }
+.media-grid.justified .media-tile { aspect-ratio: var(--ar); flex: var(--ar) 1 calc(var(--ar) * var(--row-h)); min-width: 0; }
+.media-grid.justified::after { content: ''; flex: 1000 1 0; }
+/* the picture fills the tile without sizing it: a frame-grab <video>'s own
+ 320px would widen its tile, and the row's height with it */
+.media-grid.justified .media-tile > img, .media-grid.justified .media-tile > video { position: absolute; inset: 0; }
+@media (max-width: 600px) { .media-grid.justified { --row-h: 100px; } }
.media-tile img, .media-tile video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.media-tile.broken { display: none; }
.media-tile-play {
@@ -1559,7 +1663,8 @@ body::after {
}
.mp-counts .i { font-size: 24px; line-height: 1; display: flex; }
.mp-counts .i svg { width: 28px; height: 28px; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6)); }
-.mp-like.on { color: #ff4d6d; }
+/* yours: the heart goes red; the count stays white (a 🤣 of yours left only the number red) */
+.mp-like.on .i { color: #ff4d6d; }
.mp-like .i .cemoji { width: 28px; height: 28px; }
/* the emoji sheet over the media pager (z-index 200) */
.confirm-pop-backdrop.over-pager { z-index: 300; }
@@ -1674,7 +1779,10 @@ body::after {
position: absolute; left: 16px; right: 16px; bottom: var(--mp-seek-lift); z-index: 3; height: 28px;
display: flex; align-items: flex-end; touch-action: none; cursor: pointer; visibility: hidden; opacity: 0;
}
-.mp-seek.ready { visibility: visible; opacity: 1; transition: opacity .18s; }
+/* out of sight (and out of reach: a tap there must not jump the clip) while
+ it plays; up when paused (.show), after a tap near the foot (.reveal), or held */
+.mp-seek.ready { visibility: visible; opacity: 0; pointer-events: none; transition: opacity .18s; }
+.media-pager.show .mp-seek.ready, .mp-seek.ready.reveal, .mp-seek.ready.drag { opacity: 1; pointer-events: auto; }
/* only the clip on screen's bar, and not while the pager is in motion (a
finger, a glide, a flight): it goes at once and fades back in on landing */
.mp-slide:not(.mp-cur) .mp-seek, .media-pager.moving .mp-seek { opacity: 0; transition: none; pointer-events: none; }
@@ -1854,9 +1962,107 @@ body::after {
/* Always visible, including when the media controls are tucked away. */
.mp-position { position: absolute; top: calc(env(safe-area-inset-top, 0px) + 16px); left: 50%; transform: translateX(-50%); padding: 5px 10px; border-radius: 20px; background: #0009; color: #fff; font-size: 13px; font-variant-numeric: tabular-nums; pointer-events: none; z-index: 5; }
-/* in the video pager the counter shows while the pager is in motion and
- fades out a moment after it settles (the animation restarts each time
- `moving` comes off) */
-.media-pager.videos .mp-position { animation: mp-position-out .4s 1.2s both; }
-.media-pager.videos.moving .mp-position { animation: none; opacity: 1; }
+/* the counter shows while the pager is in motion and fades out a moment
+ after it settles (the animation restarts each time `moving` comes off) */
+.media-pager .mp-position { animation: mp-position-out .4s 1.2s both; }
+.media-pager.moving .mp-position { animation: none; opacity: 1; }
@keyframes mp-position-out { from { opacity: 1; } to { opacity: 0; } }
+.media-pager.few .mp-position { visibility: hidden; }
+
+/* A call (calls.js): over every page, its picture filling it when there is
+ one; minimized, a bar along the top. */
+.call-overlay { position: fixed; inset: 0; z-index: 1000; background: #101318; color: #fff;
+ display: flex; flex-direction: column; align-items: center; justify-content: space-between;
+ padding: max(28px, env(safe-area-inset-top)) 16px max(28px, env(safe-area-inset-bottom)); }
+/* the whole of their picture, whatever its shape and the window's: a phone's
+ portrait camera filled a laptop's wide window by cropping to a forehead */
+.call-overlay .call-remote { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; display: none; }
+/* a call over the whole window: the page under it stays put */
+html:has(.call-overlay:not(.call-min)) { overflow: hidden; scrollbar-gutter: auto; }
+.call-overlay.has-video .call-remote { display: block; }
+.call-overlay.has-video .call-who { position: relative; text-shadow: 0 1px 3px rgba(0, 0, 0, .7); }
+.call-overlay.has-video .call-who .call-avatar { display: none; }
+.call-who { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 8vh; z-index: 1; }
+.call-who .call-avatar { width: 112px; height: 112px; font-size: 32px; }
+.call-name { font-size: 22px; font-weight: 650; }
+.call-status { font-size: 15px; opacity: .8; font-variant-numeric: tabular-nums; }
+.call-local { position: absolute; right: 16px; bottom: 120px; width: 28vw; max-width: 180px; aspect-ratio: 3 / 4; object-fit: cover;
+ border-radius: 12px; background: #000; box-shadow: 0 2px 12px rgba(0, 0, 0, .5); z-index: 2; }
+.call-local.mirror { transform: scaleX(-1); }
+.call-local.sharing { aspect-ratio: 16 / 10; object-fit: contain; background: #000; }
+.call-bar { display: flex; gap: 18px; z-index: 2; flex-wrap: wrap; justify-content: center; }
+.call-btn { width: 60px; height: 60px; border-radius: 50%; border: 0; display: flex; align-items: center; justify-content: center;
+ background: rgba(255, 255, 255, .16); color: #fff; cursor: pointer; padding: 0; }
+.call-btn:hover { background: rgba(255, 255, 255, .26); }
+.call-btn.on { background: #fff; color: #101318; }
+.call-btn.call-hang, .call-btn.call-decline { background: #e0245e; }
+.call-btn.call-accept { background: #1fa45a; }
+.call-overlay.call-min { inset: 0 0 auto 0; padding: 0; background: none; height: auto; justify-content: flex-start; pointer-events: none; }
+.call-min-bar { pointer-events: auto; margin: max(8px, env(safe-area-inset-top)) auto 0; display: flex; align-items: center; gap: 10px;
+ padding: 8px 16px; border-radius: 999px; border: 0; background: #1fa45a; color: #fff; font-weight: 600; cursor: pointer; box-shadow: 0 2px 10px rgba(0, 0, 0, .3); }
+.call-min-status { font-variant-numeric: tabular-nums; opacity: .9; }
+.call-min .call-audio { display: none; }
+/* a call in its conversation: a row of its own, centred, a tap calls back */
+.chat-row.call-log { justify-content: center; margin: 6px 0; flex-direction: column; align-items: center; gap: 3px; }
+.call-log-why { text-align: center; max-width: 320px; }
+.call-log-pill { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 999px;
+ border: 1px solid var(--line); background: var(--surface2); color: inherit; font-size: 13px; cursor: pointer; }
+.call-log-pill .chat-time { margin: 0; font-size: 11px; opacity: .7; }
+.call-log-pill svg { width: 16px; height: 16px; }
+.chat-row.call-log.missed .call-log-pill { color: var(--red); border-color: color-mix(in srgb, var(--red) 40%, var(--line)); }
+
+/* A post's audio/video timeline: the waveform (audio), and what was said,
+ reacted or zapped at a moment, sitting over the bar at that moment. */
+.media-timeline { padding: 24px 0 8px; white-space: normal; color: var(--text); }
+.note-video-box .media-timeline:not(.has-marks) { display: none; }
+.media-time { font-variant-numeric: tabular-nums; }
+.media-seek { position: relative; height: 42px; --played: 0%; }
+.media-wave { position: absolute; inset: 3px 0; display: flex; align-items: center; gap: 2px;
+ background: linear-gradient(to right, var(--accent) var(--played), var(--line) var(--played)); }
+.media-wave:empty { height: 4px; top: 19px; }
+.media-wave i { flex: 1; background: var(--accent); border-radius: 2px; min-width: 1px; }
+.has-wave .media-wave { background: none; }
+.has-wave .media-wave::after { content: ''; position: absolute; inset: 0 0 0 var(--played); background: var(--bg, #fff); opacity: .55; pointer-events: none; }
+.media-seek input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: .01; cursor: pointer; }
+.media-seek:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
+.media-marks { position: absolute; left: 0; right: 0; top: -22px; height: 22px; pointer-events: none; }
+.media-mark, .mp-mark {
+ position: absolute; bottom: 0; transform: translateX(-50%); padding: 0 2px; margin: 0; border: 0; background: none;
+ font-size: 16px; line-height: 1; cursor: pointer; pointer-events: auto;
+}
+.media-mark .cemoji, .mp-mark .cemoji { width: 20px; height: 20px; vertical-align: bottom; }
+/* the pager's: above its seek bar, there when the bar is */
+.mp-marks { position: absolute; left: 0; right: 0; bottom: 10px; height: 26px; pointer-events: none; }
+.mp-mark { font-size: 20px; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6)); }
+.mp-mark .cemoji { width: 24px; height: 24px; }
+.mp-seek-time { max-width: calc(100% - 16px); overflow: hidden; text-overflow: ellipsis; }
+.mp-seek.say .mp-seek-time { opacity: 1; }
+.mp-marks:not(:empty) ~ .mp-seek-time { bottom: 42px; }
+/* ...and a moment the clip plays through floats up off the bar */
+.mp-pop {
+ position: absolute; bottom: calc(var(--mp-seek-lift) + 12px); z-index: 4; transform: translateX(-50%);
+ font-size: 30px; line-height: 1; pointer-events: none; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .6));
+ animation: mp-pop 1.6s ease-out forwards;
+}
+.mp-pop .cemoji { width: 34px; height: 34px; }
+@keyframes mp-pop {
+ 0% { opacity: 0; transform: translate(-50%, 8px) scale(.6); }
+ 15% { opacity: 1; transform: translate(-50%, 0) scale(1.1); }
+ 100% { opacity: 0; transform: translate(-50%, -90px) scale(1); }
+}
+@media (prefers-reduced-motion: reduce) { .mp-pop { animation: none; opacity: 0; } }
+
+.media-moment-detail { overflow-wrap: anywhere; max-height: 72px; overflow-y: auto; margin-top: 5px; }
+.media-moment-detail:empty { display: none; }
+.live-watching.has-who { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
+.live-who { display: flex; flex-wrap: wrap; gap: 6px; }
+.live-who-p { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px 3px 3px; border-radius: 999px; background: var(--bg2, rgba(127,127,127,.12)); border: 0; color: inherit; cursor: pointer; }
+
+/* a gift's note: the sender's words above the amount, and the field for them */
+.gift-note {
+ margin: 0; padding: 8px 12px; max-width: 100%;
+ border-left: 3px solid var(--accent); border-radius: 4px;
+ background: color-mix(in srgb, var(--accent) 8%, transparent);
+ font-style: italic; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere;
+}
+.gift-note-input { width: 100%; min-height: 0; font-family: var(--sans); }