/* Chat polish (John, 2026-09-24, from a photo of his own phone). Loaded last, so it is the final word on the
   chat screen. What it fixes: a date and time under every bubble, tall holes between speakers, his own
   messages being the faintest thing on screen, the photo time wrapping, reactions colliding with the time,
   a teardrop send button in the screen's rounded corner, and a double ring round the message box. */

:root {
  --me-bubble: linear-gradient(160deg, #2f9bff 0%, #1d6fe6 100%);
  --me-bubble-flat: #2379ee;
  --them-bubble: #223342;
}

/* Rhythm: a run of quick messages sits tight; a change of speaker gets air. */
.message-stream { gap: 0; padding: 14px 2px 10px }
.message-stream > .msg-row { margin-top: 3px }
.message-stream > .msg-row.run-start { margin-top: 14px }
.message-stream > .date-divider { margin: 18px 0 4px }
.message-stream > .date-divider:first-child,
.message-stream > .load-earlier + .date-divider { margin-top: 4px }
.message-stream > .msg-status { margin-top: 3px }

/* His messages are the strongest colour on the screen, with white text. */
.bubble.outgoing {
  background: var(--me-bubble);
  background-color: var(--me-bubble-flat);
  color: #fff;
  box-shadow: 0 1px 0 #ffffff26 inset, 0 6px 18px -10px #1d6fe6aa;
}
.bubble.outgoing .reply-preview { color: #e8f2ff; border-color: #ffffff66; background: #ffffff1f }
.bubble.outgoing a { color: #fff; text-decoration: underline }
.bubble.incoming { background: var(--them-bubble); color: #eef4f8 }
.bubble.outgoing.request { background: #1b4c86; color: #e6f0ff }

/* Bubble shape follows the run: rounded all round, with the speaker's side tucked in between messages. */
.message-stream .bubble { border-radius: 20px }
.message-stream .bubble.incoming:not(.run-start) { border-top-left-radius: 6px }
.message-stream .bubble.incoming.run-end { border-bottom-left-radius: 6px }
.message-stream .bubble.outgoing:not(.run-start) { border-top-right-radius: 6px }
.message-stream .bubble.outgoing.run-end { border-bottom-right-radius: 6px }
.message-stream .bubble.photo { border-radius: 16px }
.message-stream .bubble.photo.outgoing,
.message-stream .bubble.photo.incoming { background: none; box-shadow: none }

/* One time per run, small and quiet, never wrapping (a photo row is narrower than "12:29 PM"). */
.msg-row > .bubble-time,
.bubble-time {
  font-size: 12px;
  line-height: 1.2;
  color: var(--muted);
  opacity: .8;
  white-space: nowrap;
  margin-top: 4px;
  padding: 0 6px;
}
.msg-status { font-size: 12px; opacity: .8; padding: 0 6px }

/* A reaction is a chip hooked onto the bubble's lower inner corner, clear of the time below it. */
.msg-row { position: relative }
.msg-row > .msg-reaction {
  position: relative;
  z-index: 1;
  margin: -10px 10px 0;
  padding: 2px 6px;
  font-size: 14px;
  border-radius: 999px;
  background: #16242f;
  border: 1.5px solid #0a1622;
  box-shadow: 0 2px 6px #0006;
}
.msg-row.incoming > .msg-reaction { align-self: flex-end }
.msg-row.outgoing > .msg-reaction { align-self: flex-start }

/* Composer: clear of the rounded screen corners and the home bar whatever the keyboard is doing, one
   quiet ring on the message box, and a round send button that looks pressable. */
.composer {
  gap: 8px;
  padding-left: 4px;
  padding-right: 4px;
}
.composer #message,
.composer input {
  border: 1px solid #ffffff22;
  background: #0f1d28;
  transition: border-color .15s, background-color .15s;
}
.composer #message:focus,
.composer #message:focus-visible {
  outline: none;
  border-color: #3a9cff;
  background: #122331;
}
.composer button[type=submit] {
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 50%;
  aspect-ratio: 1;
  background: var(--me-bubble);
  background-color: var(--me-bubble-flat);
  color: #fff;
  box-shadow: 0 6px 16px -8px #1d6fe6;
}
.composer button[type=submit]:disabled { opacity: .45; box-shadow: none }
.composer .attach,
.composer .voice-record { width: 44px; height: 44px; flex: none }

/* The keyboard layout hid the tab bar and let the composer sit on the screen's bottom edge. When iOS
   keeps that layout after the keyboard is gone (it can, after a send), the send button landed in the
   rounded corner. --composer-safe is 0 while a keyboard is really up and the home-bar inset otherwise. */
body.keyboard-open .conversation { padding-left: 12px; padding-right: 12px }
body.keyboard-open .composer { padding-bottom: calc(8px + var(--composer-safe, env(safe-area-inset-bottom))) }

/* ---- Openers: three first lines above an empty composer or the introduction box ---------------------- */
.openers {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  padding: 8px 2px 8px;
}
.openers::-webkit-scrollbar { display: none }
.opener {
  flex: none;
  max-width: 78%;
  min-height: 40px;
  padding: 9px 14px;
  border-radius: 999px;
  border: 1px solid #3a9cff66;
  background: #3a9cff1a;
  color: #dcecff;
  font-size: 14px;
  line-height: 1.25;
  text-align: left;
  white-space: normal;
}
.opener.picked { background: #3a9cff; color: #fff; border-color: transparent }
.opener.nudge { animation: opener-nudge 1.2s ease 2 }
@keyframes opener-nudge { 0%, 100% { transform: none } 30% { transform: translateY(-3px) } 60% { transform: translateY(1px) } }
.intro-inline .openers { margin: 2px 0 4px }

/* ---- Tap back --------------------------------------------------------------------------------------- */
.tap-float {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 3;
  font-size: 20px;
  line-height: 1;
  filter: drop-shadow(0 2px 4px #0009);
  animation: tap-bob 1.6s ease-in-out infinite;
  pointer-events: none;
}
.grid-profile { position: relative }
@keyframes tap-bob { 50% { transform: translateY(-3px) scale(1.06) } }
.tapped-you {
  margin: 4px 0 10px;
  padding: 10px 12px;
  border-radius: 14px;
  background: linear-gradient(135deg, #ff6b3d26, #ff3d7a1f);
  border: 1px solid #ff7a4d55;
  color: #ffe3d6;
  font-size: 14px;
  text-align: center;
}
.activity-quick.tap-back { background: linear-gradient(135deg, #ff7a3d, #ff3d7a); color: #fff; border: 0 }

/* ---- It's mutual ------------------------------------------------------------------------------------ */
.mutual-moment {
  position: fixed;
  inset: 0;
  margin: 0;
  border: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  color: inherit;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: calc(24px + env(safe-area-inset-top)) 20px calc(24px + env(safe-area-inset-bottom));
  background: radial-gradient(circle at 50% 42%, #1d6fe633, #050b12e6 70%);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  transition: opacity .22s ease;
}
.mutual-moment.show { opacity: 1 }
.moment-card {
  width: min(340px, 100%);
  padding: 26px 22px 16px;
  border-radius: 28px;
  background: linear-gradient(180deg, #13263a, #0c1824);
  border: 1px solid #3a9cff44;
  box-shadow: 0 30px 80px -20px #000, 0 0 0 1px #ffffff08 inset;
  text-align: center;
  transform: translateY(18px) scale(.94);
  transition: transform .45s cubic-bezier(.2, 1.4, .4, 1);
}
.mutual-moment.show .moment-card { transform: none }
.moment-card h2 {
  margin: 14px 0 6px;
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -.02em;
  background: linear-gradient(90deg, #7cc4ff, #ff7ab6);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.moment-card p { margin: 0 0 18px; color: #c9d8e6; font-size: 15px; line-height: 1.4 }
.moment-card .primary { width: 100%; min-height: 50px; font-size: 17px; font-weight: 700; border-radius: 999px; color: #fff; border: 0;
  background: linear-gradient(90deg, #2f9bff, #b14dff 60%, #ff4d94); box-shadow: 0 10px 28px -12px #ff4d94 }
.moment-card .moment-later { display: block; width: 100%; min-height: 44px; margin-top: 4px; color: #9fb3c5 }
.moment-pair { position: relative; display: flex; justify-content: center; height: 104px }
.moment-pair > span:not(.moment-sparks) { width: 96px; height: 96px; border-radius: 50%; overflow: hidden; border: 3px solid #0c1824; box-shadow: 0 0 0 2px #3a9cff; }
.moment-pair .moment-avatar, .moment-pair > span > * { width: 100%; height: 100%; object-fit: cover; border-radius: 50% }
.moment-me { transform: translateX(-60px) rotate(-8deg); opacity: 0; transition: transform .6s cubic-bezier(.2, 1.5, .4, 1) .1s, opacity .3s .1s; z-index: 1 }
.moment-him { transform: translateX(60px) rotate(8deg); opacity: 0; transition: transform .6s cubic-bezier(.2, 1.5, .4, 1) .1s, opacity .3s .1s; margin-left: -22px; box-shadow: 0 0 0 2px #ff7ab6 !important }
.mutual-moment.show .moment-me { transform: translateX(0) rotate(-6deg); opacity: 1 }
.mutual-moment.show .moment-him { transform: translateX(0) rotate(6deg); opacity: 1 }
.moment-sparks { position: absolute; left: 50%; top: 50%; width: 0; height: 0; z-index: 2 }
.moment-sparks i {
  position: absolute;
  left: 0;
  top: 0;
  font-style: normal;
  font-size: 15px;
  color: #ffd1e6;
  opacity: 0;
  transform: translate(-50%, -50%);
}
.mutual-moment.show .moment-sparks i { animation: moment-spark .9s ease-out calc(.55s + var(--t)) forwards }
@keyframes moment-spark {
  0% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a)) translateY(0) scale(.4) }
  25% { opacity: 1 }
  100% { opacity: 0; transform: translate(-50%, -50%) rotate(var(--a)) translateY(calc(-1 * var(--d))) scale(1) }
}
@media (prefers-reduced-motion: reduce) {
  .moment-card, .moment-me, .moment-him { transition: none }
  .mutual-moment.show .moment-sparks i, .tap-float, .opener.nudge { animation: none }
}

/* An open chat with nothing in it yet says what to do, instead of a screen of empty. */
.chat-empty { margin: auto 0 8px; padding: 22px 18px; text-align: center; color: #b9cada }
.chat-empty strong { display: block; font-size: 20px; color: #fff; margin-bottom: 4px }
.chat-empty span { font-size: 14px }

/* A slimmer chat header: more of the conversation on screen, his face in a circle like on the radar. */
.conversation-header { padding-bottom: 10px }
.conversation-header .avatar { width: 52px; height: 52px; border-radius: 50% }
.conversation-header .avatar img,
.conversation-header .avatar > img:not(.photo-sheet):not(.body-tile):not(.photo-tile) { border-radius: 50% }

/* Content scrolled up under the clock and the Dynamic Island read as text behind the status bar (visual tour,
   2026-09-24). A solid strip the height of the top safe area keeps that band clean on every screen. */
body::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: env(safe-area-inset-top);
  background: #07121c;
  z-index: 60;
  pointer-events: none;
}

/* The tab bar's "you are here" dash sat 3 px above the bar, over the content scrolling past (visual tour). */
.bottom-nav button[aria-current=page]:before { top: -10px }

/* Grid tiles: the distance-and-type line was hard-cut mid-word ("3.0 km WSW · Regu"); it ends in an ellipsis. */
.grid-overlay { min-width: 0 }
.grid-overlay .grid-meta { display: block; max-width: 100%; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
/* Since the whole-kilometre bands ("1 to 2 km N") the type never fit beside the distance on a three-column grid and
   every tile ended "· Ot…" (fresh eyes v2, and likely John's "word wraps" report). The type takes its own line. */
.grid-overlay .grid-type { margin-top: 0; opacity: .85 }

/* Help's close button drew a square focus box round a round-looking icon. */
.icon-button[data-help-close] { border-radius: 50% }

/* Me sections: each label sat right on the input above it, and the note under the names read as loud as the
   fields (visual tour). Labels get air above them; notes go small and muted. */
.me-acc .acc-pad > label[for],
.me-acc .acc-pad > .field-row > label[for] { display: block; margin: 18px 0 8px }
.me-acc .acc-pad > label[for]:first-child { margin-top: 2px }
.me-acc .acc-pad .field-note { margin: 10px 0 4px; font-size: 13px; line-height: 1.45; color: var(--muted) }

/* Grid mode: the Right now row picked up the page's side padding on top of its own and sat 18 px further in than
   the filters under it (visual tour). Same left edge as everything else now. */
.discover:has(.profile-grid) .intention-bar{padding-left:0;padding-right:0}

/* Filters sheet status read "Off13 shown": the two parts had no space between them. */
.filter-modal-status{display:flex;align-items:baseline;gap:8px}

.mutual-moment::backdrop { background: transparent }
.mutual-moment:not([open]) { display: none }

/* ---- Me as pages ------------------------------------------------------------------------------------ */
/* Root: the identity card and a list of rows. Nothing to save there, so no Save bar. */
.me-section:not(.section-open):has(.me-acc) .profile-save-bar,
.me-section:not(.section-open):has(.me-acc) #profile-saved { display: none }
/* A section open is a page of its own. */
.me-section.section-open > h1,
.me-section.section-open > .me-identity,
.me-section.section-open > .invite-cta,
.me-section.section-open > .anon-cta,
.me-section.section-open > .account-group,
.me-section.section-open .me-acc:not(.open) { display: none }
.me-section.section-open .me-acc.open { border: 0; background: none; box-shadow: none; margin-top: 0 }
.me-section.section-open .me-acc.open > .acc-head {
  position: relative;
  z-index: 5;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: 10px;
  margin: 0 -18px 6px;
  padding: 12px 18px;
  background: #07121cf2;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid #ffffff14;
  border-radius: 0;
}
.me-section.section-open .me-acc.open > .acc-head > span { font-size: 20px; font-weight: 800 }
.me-section.section-open .me-acc.open > .acc-head > span small { display: none }
.me-section.section-open .me-acc.open > .acc-head > i { transform: rotate(180deg); font-size: 26px; line-height: 1; color: var(--ice); opacity: 1 }
.me-section.section-open .me-acc.open > .acc-body .acc-pad { padding-left: 0; padding-right: 0 }

/* ---- Albums as containers ----------------------------------------------------------------------------- */
.album-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 12px 0 }
.album-card { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; padding: 0; text-align: left; color: inherit; background: none; border: 0 }
.album-card .album-cover { width: 100%; aspect-ratio: 4 / 3; border-radius: 16px; overflow: hidden; display: grid; place-items: center; background: #13232f; border: 1px solid #ffffff1a; margin-bottom: 6px }
.album-card .album-cover img { width: 100%; height: 100%; object-fit: cover }
.album-card .album-cover i { font-style: normal; font-size: 30px; color: #7cc4ff }
.album-card strong { font-size: 16px }
.album-card small { font-size: 13px; color: var(--muted); line-height: 1.35 }
.album-card.ghost .album-cover { border-style: dashed; background: transparent }
.album-room { font-size: 13px; color: var(--muted); margin: 6px 0 0 }
.album-bar { display: flex; align-items: center; gap: 10px; margin: 2px 0 10px }
.album-bar h2 { margin: 0; flex: 1; font-size: 20px }
.album-back { font-size: 16px; color: var(--ice); padding: 8px 0; min-height: 44px }
.album-photos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px }
.album-photo { position: relative; padding: 0; aspect-ratio: 1; border-radius: 12px; overflow: hidden; border: 0; background: #13232f }
.album-photo img { width: 100%; height: 100%; object-fit: cover; display: block }
.album-photo .lock { position: absolute; right: 6px; bottom: 6px }
.album-photos .add-photo { aspect-ratio: 1; border-radius: 12px }
.album-empty { margin-top: 12px }
.photo-options { display: grid; gap: 10px }
.photo-options-preview { width: 120px; height: 120px; object-fit: cover; border-radius: 16px; justify-self: center }
.photo-move-albums { display: flex; flex-wrap: wrap; gap: 8px }
.type-chip-button { min-height: 40px; padding: 8px 14px; border-radius: 999px; border: 1px solid #ffffff2a; background: #13232f; color: inherit }
.photo-order { display: grid; grid-template-columns: 1fr 1fr; gap: 8px }
.danger-text { color: #ff8a8a; min-height: 44px }
.album-name-form { display: grid; gap: 10px }
/* Photos save as you go and Swap or show is information: no Save bar on those pages. */
.me-section.section-open:has(.me-acc.open[data-acc=photos]) .profile-save-bar,
.me-section.section-open:has(.me-acc.open[data-acc=visibility]) .profile-save-bar { display: none }
/* Inside an album, the album's own bar is the page head (its back goes to the album cards). */
.me-section.section-open .me-acc.open:has(.album-view) > .acc-head { display: none }
.album-view .album-bar { margin: 0 0 8px; padding: 0 0 6px; border-bottom: 1px solid #ffffff14 }

/* Settings, grouped like a phone's settings. */
.settings-section .settings-group { margin: 30px 0 4px; font-size: 13px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #7cc4ff }
.settings-section .settings-head + .settings-group, .settings-section .settings-head ~ .settings-group:first-of-type { margin-top: 10px }
/* The Settings index: four rows that open pages, stacked like Me's list. */
.settings-index > .settings-head + .settings-row { margin-top: 18px }
.settings-index > [data-settings-page] + [data-settings-page] { margin-top: 10px }

/* One Filters button in the bar; the toggles that used to crowd it sit at the top of the sheet. */
.discovery-filters .filters-chip.solo { display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 8px 16px; border-radius: 999px }
.filters-chip.solo svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round }
.filters-chip.solo b { min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--ice); color: #0a1622; font-size: 12px; display: inline-grid; place-items: center }
.filter-toggles { display: grid; gap: 4px; margin: 6px 0 14px; padding-bottom: 12px; border-bottom: 1px solid #ffffff14 }

/* Profile to profile: arrows on his photo, a count, and a short slide as the next one comes in. */
.hero-photo { position: relative }
.profile-step { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; width: 40px; height: 56px; border: 0; border-radius: 12px; background: #0006; color: #fffd; font-size: 26px; line-height: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px) }
.profile-step.prev { left: 8px }
.profile-step.next { right: 8px }
.profile-count { position: absolute; top: 10px; right: 10px; z-index: 3; padding: 3px 9px; border-radius: 999px; background: #0009; color: #fff; font-size: 12px; font-weight: 600 }
dialog.slide-next { animation: sheet-slide-next .3s ease-out }
dialog.slide-prev { animation: sheet-slide-prev .3s ease-out }
dialog.edge-bump { animation: sheet-edge .3s ease-out }
@keyframes sheet-slide-next { from { transform: translateX(28%); opacity: .4 } to { transform: none; opacity: 1 } }
@keyframes sheet-slide-prev { from { transform: translateX(-28%); opacity: .4 } to { transform: none; opacity: 1 } }
@keyframes sheet-edge { 30% { transform: translateX(-10px) } 60% { transform: translateX(8px) } }
@media (prefers-reduced-motion: reduce) { dialog.slide-next, dialog.slide-prev, dialog.edge-bump { animation: none } }

/* Polish pass 2026-09-25. View my profile sits under the identity card on Me's list; pages hide it with the card. */
.me-section > .view-my-profile { display: block; width: 100%; margin: 4px 0 18px; min-height: 44px }
.me-section.section-open > .view-my-profile { display: none }
/* Me pages save on the way back; the only thing left of the old Save bar is where a failed save explains itself. */
.me-page-error:empty { display: none }
.me-page-error { margin: 14px 0 0 }
/* The help panel opens on the current screen's topics; the rest sit under a quiet divider. */
.help-body .help-more { margin: 18px 0 6px; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted) }
/* Desktop: the header logo grew to its full 132 px art on every screen wider than 560 px (height:auto won over
   the 34 px rule), covering Me's title and the page back bar. Fixed height, and it may only shrink sideways. */
.app-header .wordmark img { height: 34px; width: auto; max-width: 100%; object-fit: contain; object-position: left center }
@media (max-width:560px) { .app-header .wordmark img { height: 28px } }

/* Breathing room (John, 2026-09-25): text never sits against a field or a button. Measured by
   tests/browser/breathing_room.py; every rule below answers a spot it found. */
/* Selects are drawn by us, so Safari/WebKit cannot shrink them to a 23 px strip (it ignored min-height on the
   native menulist; Chrome did not). Same height, border and radius as the text boxes beside them. */
#main select, dialog select {
  -webkit-appearance: none; appearance: none; min-height: 44px; padding: 9px 38px 9px 12px; border-radius: 10px;
  background-color: #122330; border: 1px solid #3c4e5d; color: var(--ink); font-size: 16px; line-height: 1.3;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%239fdcff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 12px 8px;
}
/* Stats: a label is a heading for the field under it, with room above and a clear gap below. */
.stats-field .stat-grid > label:not(.stat-toggle) { margin: 12px 0 2px }
.stats-field .stat-grid { row-gap: 8px }
@media (max-width:560px) { .stats-field .stat-grid > label:not(.stat-toggle) { margin: 14px 0 0 } }
.stats-field > label[for=stat-about] { display: block; margin: 22px 0 8px }
.birthdate-echo, #birthdate-echo { display: block; margin-top: 8px; line-height: 1.4 }
/* My type & who I'm into: the live count line, Everyone/Clear and the age hint each get their own air. */
.type-live { margin: 12px 0 10px }
.type-quick { margin: 4px 0 6px }
.age-range-field .type-hint { margin-top: 12px }
/* Right now: the title no longer rests on the choices, and "Show for" stays on one line. */
dialog:has(.intention-sheet) #dialog-title { margin-bottom: 16px }
.intention-sheet .intention-controls label { white-space: nowrap }
/* Activity: the page title and its Incoming/Sent switch. */
h1 + .activity-tabs { margin-top: 14px }
/* Help: a topic's last line and the next topic's header. */
.help-body .acc + .acc, .help-body .help-more + .acc { margin-top: 8px }
.help-body .acc.open .acc-pad { padding-top: 8px; padding-bottom: 12px }

/* Design pass (2026-09-25, phone rigs + desktop Chrome). */
/* The Filters pill lost its top and bottom edge: the row was a fixed 36 px, the pill 40-44 px, and a row that
   scrolls sideways clips vertically too. The row now grows to fit its tallest control. */
.discovery-filters, .discover > .discovery-filters { height: auto; min-height: 48px; padding-top: 2px; padding-bottom: 2px; box-sizing: border-box }
/* Location blocked on a wide screen: the retry button ran the full width of the window. */
.location-denied-retry { width: auto; max-width: min(420px, calc(100% - 32px)); margin-inline: auto }
/* Help: paragraphs and the legend's closing note get their own line of air; the round close keeps a round ring. */
.help-body p + p, .help-body .legend + p { margin-top: 10px }
.help-head [data-help-close] { width: 44px; height: 44px; min-width: 44px; padding: 0; aspect-ratio: 1 }
/* Filters: the age note reads like the rest of the sheet, left-aligned. */
.filter-toggles + .connection-note, dialog .connection-note.age-note { text-align: left }
/* Your move count: a badge, not a box that looks like a stuck focus ring. */
.your-move .list-heading span { display: inline-grid; place-items: center; min-width: 22px; height: 22px; padding: 0 7px; border-radius: 999px; font-size: 13px }
/* Desktop chat beside the grid: the header's status line stays on one line instead of wrapping under the name. */
.conversation-header > div { min-width: 0 }
.conversation-header > div p, .conversation-header > div h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
/* View my profile: the preview says what men see and what stays private. */
.own-preview .own-private { margin: 14px 0 0; font-size: 13px; line-height: 1.5; color: var(--muted) }
.own-preview .bio.own-about { text-align: left; white-space: pre-line }
.own-preview .profile-interests { margin-top: 12px }
.own-preview .own-actions { display: grid; gap: 10px; margin-top: 18px }
/* A chosen type chip is light ice; its count was pale on pale and nearly invisible. Dark on light instead. */
.type-chip input:checked ~ span .type-count { background: #0a162226; color: #0a1622 }

/* Demo mode (2026-09-25): example men are marked and the screen says so. */
.demo-banner { display: flex; align-items: center; gap: 10px; margin: 6px 14px 4px; padding: 8px 12px; border-radius: 12px; background: #ffe45c1a; border: 1px solid #ffe45c55; color: #fff3b0; font-size: 13px; line-height: 1.4 }
.demo-banner span { flex: 1 1 auto } .demo-banner b { color: #ffe45c }
.demo-banner .text-button { flex: 0 0 auto; color: #ffe45c; font-weight: 700; min-height: 36px }
.grid-demo { background: #ffe45c; color: #0a1622 }
.empty .demo-open { display: block; margin: 14px auto 0; min-height: 44px; color: var(--ice) }

/* Sharing sheet, two rows not seven (2026-09-25). */
.share-summary { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 4px 0 14px }
.share-chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; min-height: 26px }
.share-chip { padding: 3px 10px; border-radius: 999px; background: #39dfff22; border: 1px solid #39dfff55; color: #d7f4ff; font-size: 13px }
.share-hint { display: block; margin-top: 6px; color: var(--muted); font-size: 12px; line-height: 1.4 }
.share-global-note { margin-top: 14px }
.share-older { margin-top: 14px } .share-older summary { min-height: 40px; display: flex; align-items: center; color: var(--muted); cursor: pointer }
.share-older summary::after { content: '›'; margin-left: auto; font-size: 22px; line-height: 1; transition: transform .15s }
.share-older[open] summary::after { transform: rotate(90deg) }
.share-sheet .reveal-signature { margin: 0 0 6px; color: #ffe45c; font-weight: 800 }
.who-sees .health-legend { display: block; margin: 16px 0 8px; font-weight: 700 } .who-sees .health-legend small { display: block; font-weight: 400; color: var(--muted); margin-top: 2px }
.vis-chip input:checked + span { background: var(--ice); color: #0a1622 }
.tap-flavours{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.55rem;align-items:center}
.tap-flavours .tap-main{min-height:48px}
.tap-flavours .flavour-chips{display:flex;gap:.35rem}
.tap-flavours .flavour-chips button{min-width:42px;min-height:42px;padding:.45rem;border-radius:999px}
.tap-flavours .greeting-note{grid-column:1/-1}
.who-sees .type-hint { margin-top: 12px }
.who-sees .type-options { margin-bottom: 4px }

/* The ⋯ menu names the consequence under each verb (fresh eyes, 2026-09-26: nobody could predict which he notices). */
.more-menu button small{display:block;font-size:12px;line-height:1.25;color:var(--muted);font-weight:400;margin-top:2px}

/* Take back beside each thing he sees, on the profile card (2026-09-26). */
.take-back-list{display:flex;flex-direction:column;gap:4px}
.take-back-row{display:flex;align-items:center;justify-content:space-between;gap:8px}
.take-back-row small{font-size:11px;color:var(--muted);font-weight:400}

/* T1: the picked chat photo, before Send (2026-09-27). */
.chat-photo-preview{display:block;max-width:100%;max-height:55vh;margin:0 auto 12px;border-radius:14px;object-fit:contain}
/* U2: the blocker's read-only copy of the chat (2026-09-27). */
.access-row span small{display:block;font-size:12px;color:var(--muted);font-weight:400}
.kept-chat{display:flex;flex-direction:column;gap:6px;max-height:50vh;overflow-y:auto;margin:0 0 12px}
.kept-message{margin:0;padding:8px 10px;border-radius:12px;background:rgba(255,255,255,.06);align-self:flex-start;max-width:85%;overflow-wrap:anywhere}
.kept-message.mine{align-self:flex-end;background:rgba(255,255,255,.12)}
.kept-message small{display:block;font-size:11px;color:var(--muted)}
/* Voice notes go after 90 days, chat photos after 3 years; the message stays as a note (R2, 2026-09-27). */
.bubble.expired{font-style:italic;color:var(--muted)}
/* Drop a pin (John, 2026-09-27): the map in the meeting-place sheet, the card's snapshot, and the 📍 on the radar. */
.pin-map{position:relative;height:min(46vh,320px);border-radius:14px;overflow:hidden;background:#0e1c27;margin:4px 0 8px;touch-action:none}
.pin-map canvas{outline:none;touch-action:none}
.place-suggest{list-style:none;margin:6px 0 0;padding:4px;border-radius:12px;background:#0e1c27;border:1px solid #ffffff24;max-height:min(40vh,300px);overflow:auto}
.place-suggest li{display:flex;align-items:center;min-height:44px;padding:8px 10px;border-radius:8px;line-height:1.3;cursor:pointer}
.place-suggest li:hover,.place-suggest li[aria-selected="true"]{background:#ffffff1a}
.pin-map .pin-marker{position:absolute;left:0;top:0;z-index:2;font-size:34px;line-height:1;padding:6px 6px 0;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none;filter:drop-shadow(0 2px 3px #000a)}
.pin-shot-map{position:fixed;left:-9999px;top:0;width:240px;height:120px;pointer-events:none}
.pin-preview{position:relative;display:block;width:100%;height:120px;border-radius:10px;overflow:hidden;background:#0e1c27 repeating-linear-gradient(0deg,transparent 0 23px,#ffffff0d 23px 24px),repeating-linear-gradient(90deg,transparent 0 23px,#ffffff0d 23px 24px)}
.pin-preview img{display:block;width:100%;height:100%;object-fit:cover}
.pin-preview i{position:absolute;left:50%;top:50%;transform:translate(-50%,-100%);font-style:normal;font-size:26px;line-height:1;filter:drop-shadow(0 2px 2px #000a)}
.meet-pin{position:absolute;z-index:7;transform:translate(-50%,-100%);display:flex;flex-direction:column;align-items:center;gap:2px;padding:0;background:none;border:0;color:#fff;min-width:44px;min-height:44px;justify-content:flex-end}
.meet-pin span{font-size:28px;line-height:1;filter:drop-shadow(0 2px 3px #000c)}
.meet-pin small{order:-1;max-width:220px;padding:2px 8px;border-radius:10px;background:#0a1622e6;border:1px solid #ffe45c88;font-size:11px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.meet-dot{position:absolute;left:50%;top:50%;z-index:7;width:32px;height:32px;padding:0;background:none;border:0;font-size:16px;line-height:32px;text-align:center;filter:drop-shadow(0 1px 2px #000c)}

/* The chat's + (2026-09-28): one button where the photo button and mic were, and its sheet of things to add. */
.composer .chat-plus svg { width: 22px; height: 22px; stroke-width: 2.2 }
.chat-adds { display: grid; gap: 6px; margin-top: 4px }
.chat-add { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 56px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 14px; background: var(--panel2); color: inherit; text-align: left; font: inherit; cursor: pointer }
.chat-add:focus-visible { outline: 2px solid #3a9cff; outline-offset: 2px }
.chat-add-icon { flex: none; width: 28px; font-size: 22px; line-height: 1; text-align: center }
.chat-add-text { display: flex; flex-direction: column; min-width: 0 }
.chat-add-text strong { font-size: 16px; font-weight: 600 }
.chat-add-text small { font-size: 13px; line-height: 1.3; color: var(--muted); margin-top: 2px }

/* Status (John, 2026-09-28): one controls row on Nearby, Status top left, Filters top right. A long status of his own
   shrinks with an ellipsis before it can push Filters off the row. */
.discovery-filters .status-chip { flex: 0 1 auto; min-width: 0; margin-right: auto; min-height: 44px; padding: 0 14px }
.discovery-filters .filters-chip.solo { min-height: 44px }   /* same height as Status beside it, and a full thumb target */
.status-chip span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.status-chip i, .status-chip small { flex: none; white-space: nowrap }
.status-custom { display: grid; gap: 8px; margin: 14px 0 4px; padding: 12px; border: 1px solid var(--line); border-radius: 14px }
.status-custom.active { border-color: #39dfff66 }
.status-custom > label { font-weight: 600 }
.status-custom input { width: 100%; min-height: 44px; box-sizing: border-box; font-size: 16px }
.status-custom-row { display: flex; align-items: center; gap: 8px }
.status-custom-row label { white-space: nowrap }
.status-custom-row select { flex: 1; min-width: 0; min-height: 44px }
.status-custom-row button { min-height: 44px; padding: 0 18px }
.status-custom-note { font-size: 12px; line-height: 1.35; color: var(--muted) }
.status-custom .form-error { margin: 0 }
.status-custom .form-error:empty { display: none }
/* Grid paging: where each page of men farther out starts, so a page fetched ahead of time still shows it arrived. */
.grid-divider { grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; padding: 12px 14px 6px; color: #9fb3c1; font-size: 13px }
.grid-divider::before, .grid-divider::after { content: ""; flex: 1; height: 1px; background: #2c4252 }
.grid-divider span { white-space: nowrap }
.grid-divider.fresh { animation: grid-divider-in .7s ease-out }
@keyframes grid-divider-in { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
.grid-more[aria-busy="true"] { color: var(--ink) }

/* The filter list is longer than a phone screen. Keep the decision controls under the thumb while the options
   scroll behind them, with the error immediately above the actions that resolve it. */
#filters-form .filter-sheet-error { margin-bottom: 8px }
#filters-form .filter-sheet-buttons {
  position: sticky;
  z-index: 4;
  bottom: calc(-22px - env(safe-area-inset-bottom));
  margin: 18px -26px calc(-22px - env(safe-area-inset-bottom));
  padding: 12px 26px calc(14px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, #192632e8, #192632 24%);
  border-top: 1px solid #334958;
  box-shadow: 0 -14px 30px #07111d99;
  backdrop-filter: blur(12px)
}
#filters-form .filter-sheet-buttons .text-button { min-width: 82px }

/* Interests are easier to scan as familiar groups than as one wall of 45 equally weighted icons. */
.hobby-groups { display: grid; gap: 24px }
.hobby-group h3 {
  margin: 0 0 10px;
  color: #a9c0cf;
  font: 700 12px/1.2 ui-monospace, monospace;
  letter-spacing: .08em;
  text-transform: uppercase
}
.hobby-group .hobby-grid { margin: 0 }
.stats-group { margin-top: 22px }
.stats-group > legend { padding-bottom: 4px }

/* A desktop grid reads as one collection. Cards keep a useful photo size and stay centred instead of stretching
   one or two profiles across the entire monitor. The end state remains attached to the collection. */
@media(min-width:900px) {
  body.grid-mode .discover { max-width: 1120px; margin-inline: auto }
  body.grid-mode .profile-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 190px));   /* auto-fit: one to four men centre over the end line */
    justify-content: center;
    gap: 12px;
    max-width: 1120px;
    margin: 18px auto 0
  }
  body.grid-mode .grid-more { max-width: 1120px; margin: 12px auto 0 }
  body.grid-mode .discover > .empty { max-width: 520px; margin: 36px auto 0 }
}

@media(max-width:420px) {
  #filters-form .filter-sheet-buttons { padding-inline: 20px }
}
.status-custom-row select { flex: 0 0 auto; width: auto; min-width: 116px }
.status-custom-row button { flex: 0 0 auto; width: auto; margin-left: auto }
/* Seven reactions since devil and splash (2026-09-28): they fit one row on a phone, and wrap rather than spill. */
.reaction-row:not(.tap-flavours){flex-wrap:wrap;gap:4px}
.reaction-row:not(.tap-flavours) .reaction-pick{width:44px;height:44px;font-size:24px}
/* Sparse Nearby (Codex design review, item 11): an empty radar's message sat a quarter of the way down the stage, far
   from the radar it describes. It now sits just above the radar (--arc-h is the stack's height, set in directionScene),
   and on a wide screen its button keeps a phone's width instead of spanning the window. */
.direction-stage>.direction-empty { top: auto; bottom: calc(100% - var(--arc-h, 75%) + 20px) }
.direction-stage>.direction-empty .invite-inline { display: block; width: 100%; max-width: 340px; margin-inline: auto }
