/* Reset, typography and the components every screen shares: buttons, fields, skeleton, sheets, toasts, banners, splash and gates.
   Layout of the screens themselves is in app.css. The keyboard rules (sheets at bottom: var(--kb), no animation that moves fixed children)
   come from js/viewport.js: see the comments marked KEYBOARD. */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body { background: var(--bg); color: var(--text); font: 400 var(--t-body)/1.5 var(--font); overflow: hidden; overscroll-behavior: none;
  -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
[hidden] { display: none !important; }
button, input, textarea, select { font: inherit; color: inherit; margin: 0; }
button { cursor: pointer; background: none; border: 0; padding: 0; touch-action: manipulation; }
h1, h2, h3, p { margin: 0; }
svg { display: block; flex: none; }
a { color: var(--accent); text-underline-offset: 2px; }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-s); }
[tabindex="-1"]:focus-visible { outline: none; }               /* headings and sheets that take focus from script are not controls: no ring */
::selection { background: var(--accent-soft); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.muted { color: var(--muted); }
.wrap-any { overflow-wrap: anywhere; }                      /* long words and URLs must never widen the page */

/* ---- buttons */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); min-height: 48px; padding: 0 var(--s5); border-radius: var(--r-l);
  font-weight: 600; line-height: 1.2; user-select: none; transition: transform var(--d1) var(--ease), background var(--d1), opacity var(--d1); }
.btn:active { transform: scale(.97); }
.btn[disabled] { opacity: .5; pointer-events: none; }
.btn.block { width: 100%; }
.btn.sm { min-height: 40px; padding: 0 var(--s4); font-size: var(--t-sm); border-radius: var(--r-m); }
.btn.primary { background: var(--accent); color: var(--accent-contrast); }
.btn.tonal { background: var(--accent-soft); color: var(--accent); }
.btn.quiet { background: var(--surface-2); color: var(--text); }
.btn.ghost { color: var(--accent); }
.btn.danger { background: var(--danger); color: var(--danger-contrast); }
.btn.danger-tonal { background: var(--danger-soft); color: var(--danger); }
@media (hover: hover) { .btn.primary:hover, .btn.danger:hover { filter: brightness(1.08); } .btn.quiet:hover { filter: brightness(.96); } }
.icon-btn { width: 44px; height: 44px; display: grid; place-items: center; flex: none; border-radius: var(--r-pill); color: var(--muted);
  transition: background var(--d1), transform var(--d1) var(--ease); }
.icon-btn:active { transform: scale(.92); }
.icon-btn.sm { width: 36px; height: 36px; }
@media (hover: hover) { .icon-btn:hover { background: var(--surface-2); color: var(--text); } }
.spinner { width: 18px; height: 18px; border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent; opacity: .8; animation: spin .8s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- fields */
.field { display: grid; gap: var(--s2); }
.field > label { font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.input, .textarea { width: 100%; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-m); padding: 13px var(--s4);
  line-height: 1.4; transition: border-color var(--d1), box-shadow var(--d1); }
.input::placeholder, .textarea::placeholder { color: var(--muted); }
.input:focus-visible, .textarea:focus-visible { outline: none; }
.input:focus, .textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
.input.secret { -webkit-text-security: disc; }              /* a pasted secret shown as dots; type=password makes iOS slow to raise the keyboard */
.textarea { resize: none; min-height: 140px; }
.field-row { display: flex; justify-content: space-between; gap: var(--s3); min-height: 20px; font-size: var(--t-meta); }
.field-error { color: var(--danger); }
.field-count { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---- loading placeholder */
.skeleton { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface) 37%, var(--surface-2) 63%); background-size: 400% 100%; border-radius: var(--r-m);
  animation: shimmer 1.4s ease infinite; }
@keyframes shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* ---- sheets: a bottom sheet on phones, a centred dialog on wide screens.
   KEYBOARD: bottom is var(--kb): on iOS the keyboard covers the bottom of the layout viewport, so the sheet is lifted by what the keyboard covers.
   Where the window itself shrinks (Telegram on Android, simulated shrink) --kb is 0 and the sheet is already above the keyboard. */
.scrim { position: fixed; inset: 0; z-index: 40; background: var(--scrim); opacity: 0; transition: opacity var(--d2) var(--ease); }
.scrim.on { opacity: 1; }
/* The page behind a sheet is blurred (native look, and it hides the page resizing behind the sheet when the keyboard comes). The scrim's opacity fades the blur in and out with it. Where the
   browser cannot blur, the scrim alone stays. If it stutters on a weak phone, lower --scrim-blur in tokens.css. */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .scrim { -webkit-backdrop-filter: blur(var(--scrim-blur)); backdrop-filter: blur(var(--scrim-blur)); }
}
.scrim:not(.on), .sheet:not(.on) { pointer-events: none; }        /* while it slides out (or before it slides in) a tap goes through to the page */
.sheet { position: fixed; z-index: 41; left: 0; right: 0; bottom: var(--kb, 0px); display: flex; flex-direction: column;
  max-height: min(calc(var(--app-h, 100vh) - var(--sat) - var(--s4)), 720px); background: var(--surface); border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--shadow); padding-bottom: var(--sab); touch-action: pan-y; transform: translateY(100%);
  transition: transform var(--d3) var(--ease), bottom var(--d2) var(--ease); }
.sheet.on { transform: none; }
/* ---- sheets with a text field (class has-field, set by ui.js). js/sheetlift.js decides WHEN; this is how they move (references/ui-pitfalls.md K14 to K19).
   While a field has the focus it sets html.kb-lift (+ --lift-full, the window's height with no keyboard), and when the keyboard starts to rise --lift-kb (the height it covers).
   The sheet is then placed from the TOP of the window: #layer becomes a box at top: 0 with a px height, the sheet sits at the bottom of it (position: absolute; bottom: <kb px>).
   Nothing depends on where the bottom of the window is, so the window shrinking under the sheet changes nothing (iOS lays position: fixed out against a layout viewport that follows
   the new height one frame late: a handover to `bottom: 0` flashed the sheet 308 px low). Nothing animated contains a percentage. The sheet keeps its height (its bottom padding is
   frozen at the value it had on focus) and so rises as one piece.
   WHY `bottom` AND NOT `transform`: iOS moves its native text caret when the page LAYS OUT. A transform runs on the compositor and lays nothing out, so the caret stayed behind and
   then jumped. `bottom` is a layout animation of one small box: cheap, and the caret follows the sheet to within 2 px.
   UP: the lower envelope of the keyboard's motion, measured on an iPhone in three openings: never ahead of it, at most 5 px behind (its 11 px shadow strip hides that).
   DOWN (the keyboard hides, the sheet stays open): the keyboard needs ~165 ms, nearly linear; the sheet takes 120 ms so that it is always ahead of it.
   BELOW the sheet: the keyboard has rounded top corners and the page shows through them. Before the window shrinks that is the page under the sheet's shadow: the sheet's own colour is
   painted 24 px below it (the first shadow). After it shrinks it is what Telegram paints under the page: tg.js paintBelow() gives that the sheet's colour for as long as the lift lasts. */
@media (max-width: 719.98px) {
  html.kb-lift #layer { position: fixed; top: 0; left: 0; right: 0; height: var(--lift-full, 100vh); }
  html.kb-lift .sheet.has-field { position: absolute; top: auto; bottom: var(--lift-kb, 0px); padding-bottom: var(--lift-pad, var(--sab));
    max-height: min(calc(var(--lift-full, 100vh) - var(--lift-want, 0px) - var(--sat, 0px) - var(--s3)), 720px);
    box-shadow: 0 24px 0 0 var(--surface), var(--shadow);
    transition: transform var(--d3) var(--ease), bottom 420ms cubic-bezier(.26, .8, .16, .96); }
  html.kb-lift-down .sheet.has-field { transition: transform var(--d3) var(--ease), bottom 120ms linear; }
  html.kb-lift-snap .sheet.has-field, html.kb-lift .sheet.has-field.instant { transition: none; }     /* the change of placement itself must not animate */
}
/* startViewport({ sheetMode: 'top' }): the alternative that needs no keyboard arithmetic at all. A sheet with a field hangs from the top of the screen, where the field is visible with
   or without a keyboard, so WebKit has nothing to scroll and nothing moves when the keyboard comes. Fine for short forms; a tall sheet should scroll inside. */
html.kb-top .sheet.has-field { top: calc(var(--sat, 0px) + var(--s3)); bottom: auto; border-radius: var(--r-xl); transform: translateY(-115%); }
html.kb-top .sheet.has-field.on { transform: none; }
html.kb-top .sheet.has-field .sheet-grip { display: none; }
.sheet.instant { transition: none; transform: none; opacity: 0; }       /* leaving while the keyboard is up: gone at once, no slide over a screen that is changing size */
.sheet-grip { padding: 10px 0 2px; display: grid; place-items: center; cursor: grab; touch-action: none; }
.sheet-grip i { width: 40px; height: 4px; border-radius: 2px; background: var(--border); opacity: .6; }
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s2) var(--s5) var(--s2) calc(var(--sal) + var(--s5)); }
.sheet-head h2 { font-size: var(--t-body); font-weight: 700; min-width: 0; }
.sheet-body { overflow: auto; min-height: 0; display: grid; gap: var(--s4); align-content: start; padding: var(--s2) calc(var(--sar) + var(--s5)) var(--s5) calc(var(--sal) + var(--s5));
  overscroll-behavior: contain; scroll-padding: var(--s6) 0; }
.sheet-foot { display: grid; gap: var(--s2); padding: var(--s2) calc(var(--sar) + var(--s5)) var(--s4) calc(var(--sal) + var(--s5)); }
@media (min-width: 720px) {
  .sheet { left: 50%; right: auto; bottom: auto; top: calc(var(--app-top, 0px) + var(--app-h, 100vh) / 2); width: min(480px, 92vw); border-radius: var(--r-xl); padding-bottom: 0; opacity: 0;   /* centred in the VISIBLE area, keyboard or not */
    transform: translate(-50%, -46%) scale(.97); transition: transform var(--d3) var(--ease), opacity var(--d2); }
  .sheet.on { transform: translate(-50%, -50%); opacity: 1; }
  .sheet-grip { display: none; }
}

/* ---- toasts. Above gates and splash (z-index), lifted above the keyboard, and moved to the top while a sheet is open so they never cover its buttons. */
.toasts { position: fixed; z-index: 90; left: 0; right: 0; bottom: calc(var(--kb, 0px) + var(--sab) + var(--fab) + var(--s6)); display: grid; justify-items: center; gap: var(--s2);
  padding: 0 var(--s4); pointer-events: none; }
#layer.sheet-open .toasts { bottom: auto; top: calc(var(--app-top, 0px) + var(--sat) + var(--s3)); }
.toast { pointer-events: auto; display: inline-flex; align-items: center; gap: var(--s2); max-width: min(92vw, 420px); padding: 11px var(--s4); border-radius: var(--r-l);
  background: var(--text); color: var(--bg); font-size: var(--t-sm); font-weight: 500; box-shadow: var(--shadow); animation: toast-in var(--d3) var(--ease) backwards; }
.toast span { min-width: 0; overflow-wrap: anywhere; }
.toast.error { background: var(--danger); color: var(--danger-contrast); }
.toast.out { opacity: 0; transform: translateY(8px); transition: opacity var(--d2), transform var(--d2); }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px) scale(.96); } }

/* ---- banner: a notice above the content (offline, stale) */
.banner { display: flex; align-items: flex-start; gap: var(--s3); padding: 12px var(--s4); border-radius: var(--r-m); background: var(--surface-2); font-size: var(--t-sm); line-height: 1.4; }
.banner svg { margin-top: 1px; color: var(--muted); }
.banner.error { background: var(--danger-soft); } .banner.error svg { color: var(--danger); }
.banner > span { flex: 1; min-width: 0; overflow-wrap: anywhere; }

/* ---- splash while the app signs in, and gates for the states where the app cannot open (not in Telegram, no access, expired, error) */
.mark { --size: 64px; width: var(--size); height: var(--size); border-radius: 22px; display: grid; place-items: center; flex: none; background: var(--accent-soft); color: var(--accent); }
.splash { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; background: var(--bg); transition: opacity var(--d3) var(--ease); }
.splash.out { opacity: 0; pointer-events: none; }
.splash .mark { animation: pulse 1.6s var(--ease) infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .55; transform: scale(.94); } }
.gate { position: fixed; inset: 0; z-index: 75; overflow-y: auto; display: grid; place-items: center; text-align: center; background: var(--bg);
  padding: calc(var(--sat) + var(--s6)) calc(var(--sar) + var(--s6)) calc(var(--sab) + var(--s6)) calc(var(--sal) + var(--s6)); }
.gate-card { display: grid; gap: var(--s3); justify-items: center; width: 100%; max-width: 360px; }
.gate h1 { font-size: var(--t-title); line-height: 1.25; }
.gate h1:focus { outline: none; }
.gate p { color: var(--muted); overflow-wrap: anywhere; }
.gate .actions { display: grid; gap: var(--s2); width: 100%; margin-top: var(--s2); }
.id-box { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); width: 100%; padding: var(--s2) var(--s2) var(--s2) var(--s4);
  border: 1px solid var(--border); border-radius: var(--r-m); background: var(--surface); }
.id-box code { font: 600 var(--t-body) var(--mono); user-select: all; -webkit-user-select: all; overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}
