/* Member app — same design language as the admin console (admin.css).
 *
 * The two were drifting apart: admin sits on the off-white ground with white
 * panels, teal uppercase eyebrows and hairline pill buttons, while this file
 * was a bare white screen with teal-bordered inputs and 2rem headings. They
 * read as different products. Everything below is admin's treatment applied
 * to the member screens.
 *
 * What is deliberately NOT copied:
 *  - Buttons are 44px, not admin's 32px. Same shape, colours and weight; a
 *    coordinator uses a mouse, a member uses a thumb.
 *  - Each screen is one card in a centred 480px column, rather than admin's
 *    two-column grid. There is only ever one thing to do here.
 *
 * Palette and scale come from tokens.css; nothing here hard-codes a colour.
 */
:root {
  --fg: var(--text);
  --fg-dim: var(--text-dim);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* An author `display` beats the UA stylesheet's `[hidden] { display: none }`,
   and the button rules here set `display: inline-flex` — so a button with the
   hidden attribute rendered anyway. That was showing "Activar notificações"
   on iOS Safari, where the code explicitly hides it. */
[hidden] { display: none !important; }
/* Was pure white, because the app had no panels and --bg only looked grey.
   It has panels now, and they need the off-white behind them to read as
   raised — the same reason admin uses it. */
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); }
body {
  font-family: var(--bn-font);
  font-weight: 300;
  font-size: var(--fs-base);
  line-height: 1.5;
  letter-spacing: -0.005em;
  /* Tabular numerals, as in admin: temperatures line up wherever they appear. */
  font-feature-settings: 'tnum' 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Fixed, on every screen, not just the check-in.
     iOS Safari does not stop touch scrolling from `overflow: hidden` on the
     body alone — the document still drags and rubber-bands, and with
     viewport-fit=cover that drag slides the brand up under the camera and
     leaves it there. Taking the body out of flow is what actually stops it.
     Anchored to the top with a dvh height rather than `inset: 0`, because the
     fixed containing block on iOS is the toolbar-hidden viewport and
     `bottom: 0` would put a button behind the toolbar. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(env(safe-area-inset-top, 0px) + 1rem) 1rem
           calc(env(safe-area-inset-bottom, 0px) + 1rem);
}
html { overflow: hidden; overscroll-behavior: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* The scroll container is #app, not the document. The screens that fit do not
   scroll at all; the ones that can outgrow a short phone — the enrol screen
   with both install banners, the note step's chips, a long circle list —
   scroll inside the column instead of dragging the whole page. overscroll
   contain stops that inner scroll from chaining back out to the document. */
#app {
  width: 100%;
  max-width: 480px;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* ── Brand ──────────────────────────────────────────────────────
   Matches admin's topbar lockup. In normal flow, not fixed: fixed meant
   nothing reserved its space and the check-in heading ran underneath the
   mark on a phone. */
.brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0 auto 1rem;
  font-size: 1.2rem;
  /* bNear black, never teal — the same lockup the landing and both consoles
     carry. This header was the last surface still setting it in the accent
     colour and the body face. */
  color: var(--bn-ink);
  /* Without this the text box's ascender/descender slack lifts "b-Near.me" off
     the mark's optical centre. */
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.brand:active { opacity: 0.7; }

/* The wordmark: b in 400, Near in 700, .me in 400, Nunito, bNear black — the
   same lockup the landing and both consoles draw. This surface used to differ
   on all three counts (one text node at 600, in the body face, in --accent),
   which is the drift CLAUDE.md recorded as the last exception. */
.brand .wordmark { font-family: var(--bn-wordmark); }
.brand .wm-b,
.brand .wm-me  { font-weight: 400; }
.brand .wm-near { font-weight: 700; }

/* Trial (2026-09-06, Sergio): b-Near.me. The hyphen is full size — the same
   as the `b` it follows — so it needs none of the compensations a shrunken
   one did: at 1em the font's own hyphen metrics already place it correctly,
   and a margin would only open a gap the kerning did not ask for. Weight 400
   as well, matching the `b` rather than Near's 700, or the dash reads as
   belonging to the bold half.

   Still its own span, and still one class per stylesheet, so the trial can be
   pulled back out by deleting the rule and the spans — and so the dash can
   stay aria-hidden, which is what makes a screen reader say the name instead
   of spelling it. */
.wm-dash { font-weight: 400; }


/* Brand on the left, language on the right — the same header shape as the
   landing and both consoles. The brand used to be centred on its own. */
.app-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: 1rem;
}
.app-head .brand { margin: 0; }

/* ── Language picker ───────────────────────────────────────────
   A dropdown at every width: the column is 480px at its widest, which never
   has room for three segments beside the brand. */
.lang-picker { position: relative; display: flex; align-items: center; flex: 0 0 auto; }
.lang-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--accent-deep);
  font-family: inherit;
  font-size: var(--fs-cap);
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1;
  cursor: pointer;
  transition: border-color .2s ease;
}
.lang-picker[data-open="true"] .lang-trigger { border-color: var(--line-hi); }
.lang-caret { width: 10px; height: 10px; transition: transform .2s ease; }
.lang-picker[data-open="true"] .lang-caret { transform: rotate(180deg); }

.lang-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  display: none;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  min-width: 88px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--card);
  box-shadow: var(--shadow-1);
}
.lang-picker[data-open="true"] .lang-menu { display: flex; }
.lang-menu button {
  appearance: none;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: var(--fs-cap);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-align: left;
  padding: 9px 14px;
  min-height: 38px;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.lang-menu button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); }
.brand .logo {
  width: 3.25rem;
  height: 3.25rem;
  display: block;
  color: var(--accent);
}

/* ── The lockup stays where the launch screen left it ───────────
   The front door puts the lockup at the foot; the app used to put it back at
   the top of the centred column, so it jumped the moment the door faded. It
   is pinned to the same offset the launch screen uses — `1.5rem` plus the
   safe area, the identical expression — so the hand-over holds still.

   CLAUDE.md records that this brand is deliberately *not* fixed, because a
   fixed one at the top left nothing reserving its space and the check-in
   heading ran underneath it. The same trap applies at the bottom, so `#app`
   reserves the height below the content.

   Check-in is the exception, as it is everywhere in this app: the brand stays
   in its header there and nothing is reserved. That screen is a flex column
   whose thermometer takes what is left, so bottom padding does not overlap it
   — it shrinks it, on the one screen that has least to spare. */
#app:not([data-state="checkin"]):not([data-state="thread"]) .app-head .brand {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(1.5rem + env(safe-area-inset-bottom));
  margin: 0;
  /* It is fixed over a column that scrolls, so it needs a ground of its own —
     without one the content passing behind it collides with the wordmark
     instead of disappearing under it. The fade is what stops that ground
     reading as a bar. */
  padding-top: 0.6rem;
  background: linear-gradient(to bottom, transparent, var(--bg) 45%);
  z-index: 3;
}
/* The brand has left the flow; without this the picker slides to the left. */
#app:not([data-state="checkin"]):not([data-state="thread"]) .app-head { justify-content: flex-end; }
/* 3.25rem is the logo, which is what sets the lockup's height. */
#app:not([data-state="checkin"]):not([data-state="thread"]) {
  padding-bottom: calc(3.25rem + 1.5rem + 0.9rem + env(safe-area-inset-bottom));
}
/* The conversation is the second screen that needs its height back: the
   composer must not be pushed under a lockup, and a thread is somewhere you
   are working rather than somewhere you are being introduced to the brand. */

/* ── The launch screen ──────────────────────────────────────────
   The front door: the mark, and the lockup at the foot. Covers everything
   until app.js knows which screen you are on, then fades out. It is in the
   markup and visible by default, so it is there on the very first frame —
   the app must never be seen before its front door.

   Removed with the `gone` class rather than by deleting the element, so the
   fade has something to fade. */
.launch {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  padding: max(1.25rem, env(safe-area-inset-top)) 1.25rem
          calc(1.5rem + env(safe-area-inset-bottom));
  transition: opacity .45s ease;
}
.launch.gone { opacity: 0; pointer-events: none; }
/* Off the accessibility tree the moment it stops mattering, and out of the
   layout entirely once the fade has run. */
.launch[hidden] { display: none !important; }
.launch-orb {
  position: relative;
  flex: 1;
  width: 100%;
  max-width: 420px;
  min-height: 200px;
}
.launch-brand { margin: 0; }

.orb-glow {
  position: absolute;
  inset: 4% 8%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 45%, rgba(15, 158, 140, 0.10), rgba(15, 158, 140, 0) 68%);
  pointer-events: none;
}
/* Stands in until three.js arrives, and stays for good if it never does. */
.orb-still { position: absolute; inset: 0; display: grid; place-items: center; }
.orb-still img { width: min(56%, 200px); height: auto; }
.launch-orb[data-live="true"] .orb-still { display: none; }
/* As on the landing: a finger dragged across the mark turns it rather than
   scrolling. Scoped to the element, so the still mark behaves normally. */
.launch-orb bnear-orb { position: absolute; inset: 0; display: block; touch-action: none; }

/* ── The notice has changed ─────────────────────────────────────
   Teal, not coral: nothing has gone wrong and nothing is being demanded —
   coral in this product means 40° and destructive actions. It sits above the
   card because it is about the app rather than about the screen, and it is
   never shown on the check-in, which has to fit exactly. */
.notice-banner {
  display: none;
  background: var(--accent-100, #e6f6f3);
  border: 1px solid var(--accent-300, #93d8cc);
  border-radius: 12px;
  padding: 1rem 1.1rem 1.1rem;
  margin-bottom: 1rem;
  text-align: left;
}
#app:not([data-state="checkin"]) .notice-banner:not([hidden]) { display: block; }
.notice-banner strong { display: block; font-size: 1.02rem; font-weight: 600; margin-bottom: 0.3rem; }
.notice-banner p { font-size: 0.9rem; line-height: 1.55; color: var(--muted); margin: 0; }
.notice-actions { display: flex; gap: 0.5rem; margin-top: 0.9rem; }
.notice-actions > * { flex: 1; justify-content: center; text-align: center; }

/* ── The card ───────────────────────────────────────────────────
   admin.css's .card, to the pixel: hairline border, 12px, soft shadow. */
.state {
  display: none;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-1);
  padding: 1.25rem 1.25rem 1.4rem;
  text-align: left;
}
#app[data-state="waiting"]   .state[data-when="waiting"],
#app[data-state="checkin"]   .state[data-when="checkin"],
#app[data-state="submitted"] .state[data-when="submitted"],
#app[data-state="already"]   .state[data-when="already"],
#app[data-state="enroll"]    .state[data-when="enroll"],
#app[data-state="picker"]    .state[data-when="picker"],
#app[data-state="savecode"]  .state[data-when="savecode"],
#app[data-state="note"]      .state[data-when="note"],
#app[data-state="messages"]  .state[data-when="messages"],
#app[data-state="thread"]    .state[data-when="thread"],
#app[data-state="circles"]   .state[data-when="circles"] { display: block; }

/* The eyebrow — admin's single most recognisable signature. */
.eyebrow {
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-deep);
  margin: 0 0 0.6rem;
  line-height: 1;
}

h1 {
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 0.35rem;
  line-height: 1.2;
}
.sub { color: var(--fg-dim); font-size: 0.9rem; margin: 0 0 1.1rem; }
.hint { color: var(--fg-dim); font-size: var(--fs-help); margin-top: 0.75rem; }

/* A screen's actions, in the shape the rest of the app uses: the primary one
   full width, like the join and install buttons, and the quieter pair sharing
   a row under it. */
.state-actions { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 1.1rem; }
.state-actions .wide { width: 100%; }
.state-actions-row { display: flex; gap: 0.5rem; }
.state-actions-row > * { flex: 1 1 0; min-width: 0; }

/* ── Consent ────────────────────────────────────────────────────
   Art. 7(2) wants the request for consent clearly distinguishable from
   everything else, in plain language. So it gets its own block with a rule
   above it rather than being small print under a button, and the checkbox is
   a real one at a real size — a 12px tick nobody can hit is not a choice. */
.consent {
  margin-top: 1.1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}
.consent-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  cursor: pointer;
  font-size: var(--fs-help);
  line-height: 1.55;
  color: var(--fg);
}
.consent-row input {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 0.1rem 0 0;
  accent-color: var(--accent);
}
.consent .hint { margin-top: 0.5rem; }
.consent a { color: var(--accent-deep); }

/* Erasure sits with leaving a circle, and has to look like the heavier of the
   two without shouting on a screen people visit for other reasons. */
.danger-zone > summary { color: var(--danger, #c0392b); }
.danger-outline {
  margin-top: 0.6rem;
  border-color: var(--danger, #c0392b);
  color: var(--danger, #c0392b);
}

/* ── The ID code ────────────────────────────────────────────────
   Shown once at registration and again on "Os teus círculos". It has to be
   readable off a screen and typeable into a box on another device, so it is
   monospaced, large, and selectable in one gesture. */
.member-code {
  margin: 1.1rem 0 0.4rem;
  padding: 0.9rem 0.6rem;
  border: 1px dashed var(--line);
  border-radius: 12px;
  background: var(--bg);
  font-family: var(--bn-mono);
  font-size: 1.25rem;
  letter-spacing: 0.06em;
  text-align: center;
  /* One tap selects the whole thing rather than a word of it. */
  user-select: all;
}
/* The recovery box is folded away: most people joining have no code, and a
   field asking for one they do not have reads as a wall. */
.recover { margin-top: 1rem; }
.recover > summary {
  cursor: pointer;
  color: var(--accent-deep);
  font-size: var(--fs-help);
  /* A summary is a control, so it needs a target height on a phone. */
  padding: 0.4rem 0;
  list-style: none;
}
.recover > summary::-webkit-details-marker { display: none; }
.recover > summary::before { content: '+ '; }
.recover[open] > summary::before { content: '- '; }

/* ── The note step ──────────────────────────────────────────────
   The optional half of a check-in. Deliberately quiet: the temperature is
   already saved by the time this shows, so nothing here may read as a step
   that must be completed. */
.field-label {
  display: block;
  margin-top: 1rem;
  font-size: var(--fs-help);
  color: var(--fg-dim);
}
.note-tags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.9rem 0 0; }
/* Chips toggle with a border and a tint, never with transform: a scaled
   element inside a section that later gets `display: none` leaves a stale
   compositor layer on mobile Safari. Same reason the selection states
   elsewhere in this app changed. */
.note-tag {
  appearance: none;
  padding: 0.45rem 0.8rem;
  min-height: 36px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-help);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.note-tag[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.note-text {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-top: 0.9rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--fg);
  font: inherit;
  resize: vertical;
}
.note-text::placeholder { color: var(--text-faint); }
.note-text:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

/* ── Buttons — admin's, at a thumb-sized height ────────────────── */
.primary-btn,
.ghost-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: 0 var(--s-5);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background 150ms ease-out, border-color 150ms ease-out, color 150ms ease-out;
}
.primary-btn {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.primary-btn:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.primary-btn:disabled,
.ghost-btn:disabled { opacity: 0.55; pointer-events: none; }
.ghost-btn:hover { background: var(--card-hi); }
/* No transform on press: a scale() on an element whose section later gets
   display:none has leaked the compositor on mobile before. */
.primary-btn:active,
.ghost-btn:active { background: var(--bg-elev); }
.primary-btn:active { background: var(--accent-deep); }

/* The two-tap leave confirmation. */
.danger-outline {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
}
.danger-outline:hover { background: var(--bn-danger-bg); }

/* Full-width actions read as the screen's one next step.
   .ghost-btn is inline-flex, so a button not named here shrinks to its label
   and sits in a stack of full-width siblings looking like a mistake — which
   is exactly what the Messages link did until it was added. Any new action in
   one of these stacks belongs on this list. */
.circles-link,
.messages-quiet,
#answerAgainBtn,
#activateNotifBtn { width: 100%; margin-top: 0.6rem; }

/* ── Inputs — admin's neutral field, not the old teal-bordered one ── */
.code-input { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }
.code-input input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-body);
}
.code-input input::placeholder { color: var(--text-faint); }
.code-input input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.code-input .primary-btn { flex: 1 1 auto; }

/* ── Thermometer ────────────────────────────────────────────────
   Geometry unchanged — the tap-to-value mapping depends on it. */
.thermometer-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  margin-top: 0.4rem;
}
.thermometer {
  /* Sized by height, not width. Width-sizing made it ~500px tall on a phone
     whatever the screen height was, which pushed Confirmar below the fold and
     forced the page to scroll. The viewBox keeps the aspect ratio. */
  height: min(48vh, 440px);
  /* iOS Safari's vh is the toolbar-hidden viewport, so vh alone still
     overflows the space actually on screen. dvh is the visible one; the vh
     line above stays as the fallback. */
  height: min(48dvh, 440px);
  width: auto;
  aspect-ratio: 280 / 400;
  touch-action: none;
  user-select: none;
  cursor: pointer;
}
.thermometer:focus { outline: none; }
.thermometer:focus-visible .tube-outline,
.thermometer:focus-visible .bulb-outline { stroke: var(--accent); }
.thermometer .tube-outline,
.thermometer .bulb-outline {
  fill: var(--accent-tint);
  stroke: var(--text-faint);
  stroke-width: 2;
}
.thermometer .mercury { transition: y 0.18s ease, height 0.18s ease, fill 0.18s ease; }
.thermometer .mercury.t-365 { fill: var(--t-365); }
.thermometer .mercury.t-370 { fill: var(--t-370); }
.thermometer .mercury.t-380 { fill: var(--t-380); }
.thermometer .mercury.t-390 { fill: var(--t-390); }
.thermometer .mercury.t-400 { fill: var(--t-400); }
.thermometer .tick-line { stroke: var(--text-faint); stroke-width: 1.5; }
.thermometer .tick-label {
  fill: var(--fg-dim);
  font-size: 16px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  dominant-baseline: middle;
}
.thermometer .tick-label.selected {
  /* The only place the current value is stated now that the big readout is
     gone, so it is sized and tinted to be read at a glance. The fill is set
     per band from JS. */
  font-size: 21px;
  font-weight: 700;
}
.thermometer .legend {
  fill: var(--text-faint);
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  dominant-baseline: middle;
  /* The eyebrow treatment, carried into the drawing. */
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.thermometer .legend.selected { fill: var(--accent-deep); font-weight: 700; }
.thermometer .tick-label,
.thermometer .legend { transition: font-size 0.12s ease, fill 0.12s ease; }

#tempConfirmBtn { min-width: 180px; font-size: var(--fs-lg); }

/* Who reads this answer — shown before it is given, not after. */
.readers {
  font-size: var(--fs-help);
  color: var(--text-faint);
  margin-bottom: 0.5rem;
}

/* ── Install banners — admin's tinted panel ────────────────────── */
.install-banner {
  background: var(--accent-tint);
  border: 1px solid var(--line-hi);
  border-radius: 12px;
  padding: 0.9rem 1.1rem;
  margin: 0 0 1rem;
  text-align: left;
  font-size: 0.85rem;
  color: var(--accent-deep);
}
.install-banner strong { display: block; margin-bottom: 0.4rem; font-weight: 600; }
.install-banner ol { margin: 0; padding-left: 1.1rem; line-height: 1.9; }
.install-banner em { font-style: normal; font-weight: 600; }
.install-banner .primary-btn { width: 100%; margin-top: 0.7rem; }

/* Was an SF Symbols character in a private-use codepoint, which draws only on
   Apple devices with SF Pro and is a blank box everywhere else — including
   the Android banner this is now shared with. Real artwork, inheriting the
   surrounding text colour and sitting on the baseline. */
.ui-glyph {
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.28em;
  display: inline-block;
}

/* Installing is not the job on the check-in screen, and that screen is pinned
   to the viewport — a banner would only take height from the thermometer. */
#app[data-state="checkin"] .install-banner,
#app[data-state="note"] .install-banner,
#app[data-state="savecode"] .install-banner { display: none; }


/* ── Circle picker ──────────────────────────────────────────────
   One button now that members no longer choose a group, so it fills
   the card the way a single next step should. */
.depts { display: grid; gap: 0.5rem; margin-top: 0.9rem; }
.depts .primary-btn { width: 100%; }

/* ── Os teus círculos — admin's .person / .circle-code row ─────── */
.my-circles { display: flex; flex-direction: column; gap: 0.4rem; margin: 0 0 0.4rem; }
.my-circle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 10px;
}
.my-circle-name { font-weight: 600; font-size: 0.95rem; line-height: 1.25; }
/* Which caregiver a circle belongs to — several of them have an "Amigos". */
.my-circle-who { font-size: var(--fs-help); color: var(--fg-dim); }
/* A circle its coordinator has paused. The member is still a member, so the
   row stays and keeps its Leave button; the flag says why no check-in of
   theirs is reaching it. Neutral, not a warning: nothing is wrong. */
.my-circle-flag {
  margin-left: 0.4rem;
  padding: 0.1rem 0.4rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg-dim);
  font-size: var(--fs-cap);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}
.my-circle .ghost-btn {
  min-height: 32px;
  padding: 0 var(--s-3);
  font-size: var(--fs-help);
  flex: 0 0 auto;
}

.join-more {
  margin-top: 1.1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}
.join-more .sub { margin-bottom: 0; }

/* ── The check-in screen fits the viewport, full stop ────────────
   Giving the thermometer a size — by width, then by vh, then by dvh — meant
   guessing how much room the text above it would take, and the guess was
   wrong on real phones. Instead the column is pinned to the viewport and the
   thermometer takes whatever height is left after the heading and the button,
   so it cannot overflow no matter how the text wraps. The card's padding is
   part of what it flexes against, so adding the panel changed nothing here.
   The lock is driven by a class that setState() puts on <html>, NOT by
   :has(). It used to be `html:has(#app[data-state="checkin"])`, which is iOS
   15.4+ — and on anything older the entire lock silently did nothing, with no
   warning and no fallback worth the name. setState is the function that sets
   the state attribute in the first place, so a class set in the same breath
   cannot be less reliable, and it works everywhere. */
/* iOS Safari does not stop touch scrolling from `overflow: hidden` on the
   body alone — the document still drags and rubber-bands, and with
   viewport-fit=cover that drag slides the brand up under the camera and
   leaves it there. Measured first: the layout does not overflow at any iPhone
   size with the notch insets applied, so there is nothing to scroll to and
   nothing to fit better. The page is simply taken out of the scrolling model.
   Anchored to the top with a dvh height rather than `inset: 0`, because the
   fixed containing block on iOS is the toolbar-hidden viewport — `bottom: 0`
   would put Confirmar behind the toolbar. */
/* The check-in goes further than the rest: it must FIT, not scroll. The
   thermometer's box is what svgYFromEvent maps the pointer against, so a
   column that scrolled under the finger would record the wrong temperature.
   Everything else on this screen is sized to whatever is left. */
html.checkin-locked #app { overflow: hidden; }
#app[data-state="checkin"] {
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
#app[data-state="checkin"] .state[data-when="checkin"] {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#app[data-state="checkin"] .thermometer-wrap {
  flex: 1;
  min-height: 0;
  width: 100%;
  justify-content: center;
}
/* The box must keep the drawing's 280:400 ratio EXACTLY. svgYFromEvent maps
   the pointer against the box, so letterboxing inside it silently reads the
   wrong temperature — the failure is a wrong answer recorded, not a visible
   glitch, which is why this is measured rather than eyeballed.
   A max- constraint cannot do it: whichever dimension is definite stays put
   while the other is clamped, so `height:100%` + `max-width` letterboxes on a
   tall screen and `width:100%` + `max-height` letterboxes on a short one.
   Both were tried; both were wrong at one end.
   The slot exists to be measured. It holds the drawing and nothing else — the
   Confirmar button is its sibling, so 100cqh is the height genuinely free —
   and the thermometer takes the smaller of what the slot offers in each
   direction, with the width always derived from the height. */
#app[data-state="checkin"] .thermo-slot {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  container-type: size;
  display: flex;
  align-items: center;
  justify-content: center;
}
#app[data-state="checkin"] .thermometer {
  /* 400/280 = 1.42857. Container units are Safari 16+; where they are not
     understood this declaration drops and the dvh rule above applies, which
     is height-led too and so also keeps the ratio. */
  height: min(100cqh, 142.857cqw);
  width: auto;
  max-width: none;
  aspect-ratio: 280 / 400;
}
#app[data-state="checkin"] #tempConfirmBtn { flex: 0 0 auto; }

/* ── Going back ─────────────────────────────────────────────────
   Back is navigation, not a step in the screen, so it sits at the head beside
   where you are — not at the bottom, where it read as a final action rather
   than a way out. Every screen in the member app that has a way back uses
   this: messages, the conversation, and your circles.

   A chevron with no label: it is the one control on a phone that needs no
   word, and a labelled pill down there was costing a line of the thread. The
   accessible name still comes from the same "back" string, through
   data-t-aria, so the three languages are unchanged. */
.screen-head {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  /* Sticky, so the way out cannot scroll out of reach. On the short screens
     this does nothing at all; on "Os teus círculos" — a circle list, two
     folded panels and a join form — it is the difference between leaving and
     scrolling back up to leave.

     Bled to the card's edges with a negative margin and the padding put back,
     or content would scroll through the gap either side of a head narrower
     than the card. The 0.75rem left padding puts the chevron on the same
     optical line as the text below it, which is where the old -0.5rem margin
     had it. */
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--card);
  margin: -1.25rem -1.25rem 0.35rem;
  padding: 1.25rem 1.25rem 0.35rem 0.75rem;
}
.screen-head .eyebrow { margin: 0; }
.screen-back {
  flex: 0 0 auto;
  /* 44px of thumb, 20px of chevron. The target is the point; the icon is not. */
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.screen-back svg { width: 20px; height: 20px; }
.screen-back:active { background: var(--bg); color: var(--text); }
.screen-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Something is waiting ───────────────────────────────────────
   A message nobody has read is the only thing on this screen that another
   person is waiting on, so it is a card and not a third button in a stack of
   buttons. It still sits *below* the check-in action: answering is what the
   app is for, and a conversation is what follows from it.

   It names who wrote and never what they wrote — the same rule the push
   notification follows, and for the same reason. */
.msg-waiting {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  margin-top: 0.5rem;
  padding: 0.8rem 0.9rem;
  text-align: left;
  font: inherit;
  color: var(--text);
  background: var(--accent-100, #e6f6f3);
  border: 1px solid var(--accent-300, #93d8cc);
  border-radius: 14px;
  cursor: pointer;
  transition: transform .15s ease;
}
.msg-waiting:active { transform: scale(0.99); }
.mw-icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;
           background: var(--accent); color: #fff; }
.mw-icon svg { width: 18px; height: 18px; }
.mw-text { display: grid; gap: 1px; min-width: 0; }
.mw-who { font-weight: 600; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mw-cta { font-size: 0.8rem; color: var(--muted); }
.mw-count {
  min-width: 22px; height: 22px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--bn-coral); color: #fff;
  font-size: 0.75rem; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* ── Messages ───────────────────────────────────────────────────
   One row per caregiver, and one screen per conversation. Bubbles rather
   than a list, because it is a conversation and the shape should say so. */
.thread-list { display: grid; gap: 0.5rem; margin: 1rem 0; }
.thread-item {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.35rem 0.75rem;
  width: 100%;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.75rem 0.9rem;
  font: inherit;
  color: var(--text);
  cursor: pointer;
}
.thread-item:active { opacity: 0.7; }
.thread-who { font-weight: 600; font-size: 0.95rem; }
.thread-last { grid-column: 1 / -1; font-size: 0.85rem; color: var(--muted);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-badge {
  align-self: start;
  min-width: 20px; height: 20px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--bn-coral); color: #fff;
  font-size: 0.7rem; font-weight: 700; font-variant-numeric: tabular-nums;
}

.thread-msgs { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.9rem 0 1rem;
               max-height: 46vh; overflow-y: auto; }
/* 16px all round with the corner nearest the speaker pulled in to 5px. That
   one asymmetry is what makes a stack of rectangles read as a conversation,
   and it costs nothing. */
.t-msg { max-width: 84%; border-radius: 16px; padding: 0.55rem 0.8rem; font-size: 0.92rem; line-height: 1.5; }
/* Theirs on the left in the accent, yours on the right in a neutral — the
   opposite of the console, because "mine" is whoever is holding the phone. */
.t-them { align-self: flex-start; background: var(--accent); color: #fff; border-bottom-left-radius: 5px; }
.t-mine { align-self: flex-end; background: var(--card); border: 1px solid var(--line); border-bottom-right-radius: 5px; }
.t-subject { display: block; font-weight: 600; margin-bottom: 2px; }
/* Furniture, not content: small, quiet, and only under the last of a run —
   a timestamp on every line turns a conversation into a log. */
.t-when { display: block; font-size: 0.68rem; opacity: 0.6; margin-top: 3px; font-variant-numeric: tabular-nums; }
.t-msg.t-run .t-when { display: none; }
.t-msg.t-run { margin-top: -0.35rem; }
/* The day, once, where it changes. */
.t-day { align-self: center; font-size: 0.7rem; color: var(--muted); letter-spacing: 0.06em;
         text-transform: uppercase; margin: 0.35rem 0 0.1rem; }

/* One row, not a stack: the composer should cost the conversation as little
   height as possible on a phone. */
.thread-reply { display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; align-items: end; }
.thread-reply .primary-btn { width: auto; padding-left: 1.1rem; padding-right: 1.1rem; }
.thread-reply textarea {
  width: 100%; font: inherit; font-size: 0.95rem; color: var(--text);
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 0.6rem 0.7rem; resize: vertical;
}
.thread-reply textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
