/* ===========================================================================
   burunduk — the token layer and the component set of every screen.

   Telegram's theme is the base; app.js (AM.t.applyTheme) re-derives every role
   a reader must read so a theme whose hint, section header or accent falls
   under 4.5:1 still lands above it. Screens never touch --tg-theme-* and never
   carry a raw hex — the fallbacks below are the one place those variables
   appear.

   Two manners, one markup: <html class="plat-ios"> (also macOS and anything
   else) gets inset grouped sections; <html class="plat-android"> gets
   full-bleed sections with the header drawn inside the block, as the Android
   client does.

   One thing is ours and not Telegram's: the sealing-wax freshness ring around
   an avatar, and the words beside it. It means knowledge freshness and nothing
   else.

   The second part of this file (prefix `a-`) is the owner's area. It adds no
   colour and no step of its own: every rule there is written in the tokens of
   the first part.
   =========================================================================== */

:root {
  /* --- surfaces: corrected and re-keyed by AM.t.applyTheme() --------------- */
  --list-page:  var(--tg-theme-secondary-bg-color, #efeff4);
  --cell:       var(--tg-theme-section-bg-color, #ffffff);
  --plain-page: var(--tg-theme-bg-color, #ffffff);
  --cell-press: rgba(128, 128, 128, .18);
  --field:      rgba(128, 128, 128, .16);
  --switch-off: rgba(128, 128, 128, .45);
  --control-border: rgba(128, 128, 128, .75);   /* re-derived per theme to >= 3:1 on the cell */
  --separator:  var(--tg-theme-section-separator-color, rgba(128, 128, 128, .35));

  /* --- text ---------------------------------------------------------------- */
  --text:           var(--tg-theme-text-color, #000000);
  --subtitle:       var(--tg-theme-subtitle-text-color, #6b6b70);
  --hint:           var(--tg-theme-hint-color, #6b6b70);
  --section-header: var(--tg-theme-section-header-text-color, #6d6d72);
  --accent:         var(--tg-theme-accent-text-color, #0a6ec2);
  --link:           var(--tg-theme-link-color, #0a6ec2);
  --destructive:    var(--tg-theme-destructive-text-color, #d3271c);

  /* --- filled things ------------------------------------------------------- */
  --button:     var(--tg-theme-button-color, #0a6ec2);
  --on-button:  var(--tg-theme-button-text-color, #ffffff);
  --pill-fill:  rgba(128, 128, 128, .16);
  --bubble-in:  var(--tg-theme-section-bg-color, #efeff4);
  --bubble-out: var(--tg-theme-button-color, #0a6ec2);

  --skeleton:         rgba(128, 128, 128, .22);
  --skeleton-on-cell: rgba(128, 128, 128, .22);

  /* --- the one colour of our own: sealing wax = knowledge freshness ---------
     Never used for anything else. The ring is flat, notched or beaded — it
     cannot be read as Telegram's blue-green segmented Stories ring. */
  --wax:       #C2412D;   /* the bead and the mini-ring mark */
  --wax-ring:  #CB5642;   /* the notched ring of a dated base */
  --wax-track: #D68272;   /* the closed track the bead runs along */
  --wax-ink:   #A6331F;   /* the freshness words, corrected to 4.5:1 */

  /* --- icon tiles: fixed hues, identical in both themes ---------------------
     The glyph is always white, so each hue clears 3:1 against white (measured:
     blue 4.02, green 3.47, orange 3.40, red 3.55, purple 4.13, gray 3.26). */
  --tile-blue:   #007AFF;
  --tile-green:  #1C9E4F;
  --tile-orange: #DD6B00;
  --tile-red:    #FF3B30;
  --tile-purple: #AF52DE;
  --tile-gray:   #8E8E93;

  /* --- type: the system stack, nothing loaded ------------------------------ */
  --ui: -apple-system, system-ui, "SF Pro Text", Roboto, "Segoe UI", sans-serif;

  /* --- space, radius, easing ----------------------------------------------- */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s6: 24px; --s8: 32px; --s12: 48px;
  --r-sec: 12px; --r-field: 10px; --r-pill: 999px; --r-bubble: 18px; --r-tile: 7px;
  --ease: cubic-bezier(.2, .7, .3, 1);

  /* --- the iOS manner (default) -------------------------------------------- */
  --sec-inset: 16px;         /* side inset of a grouped section */
  --sec-radius: var(--r-sec);
  --sec-gap: 30px;           /* page gap between two groups */
  --f-body: 17px;            /* cell titles, names, chat text, inputs */
  --f-title-track: -.4px;    /* SF tightens large titles; Roboto does not */
}

/* The Android client: sections run edge to edge, the section header is drawn
   inside the block and takes the theme's own (accent-tinted) header colour. */
.plat-android {
  --sec-inset: 0px;
  --sec-radius: 0px;
  --sec-gap: 12px;
  --f-body: 16px;
  --f-title-track: 0px;
}

/* --- base ------------------------------------------------------------------ */

* { box-sizing: border-box; }
html, body { margin: 0; background: var(--list-page); color: var(--text); }
html[data-page="plain"], html[data-page="plain"] body { background: var(--plain-page); }
body {
  font-family: var(--ui);
  font-size: var(--f-body);
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overflow-wrap: break-word;
}
button { font: inherit; color: inherit; background: none; border: 0; margin: 0; padding: 0; text-align: left; }
input, select, textarea { font: inherit; }
h1, h2, h3, p, ul, ol { margin: 0; }
ul, ol { list-style: none; padding: 0; }
/* A screen that hides a block by [hidden] must win over any display rule. */
[hidden] { display: none !important; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Headings are focus targets only, so a screen reader lands on the new screen. */
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* --- the screen container ---------------------------------------------------
   Sections carry their own side inset, so the container has none; the safe-area
   insets are still honoured for a landscape notch. */

.screen {
  min-height: var(--tg-viewport-stable-height, 640px);
  max-width: 560px;
  margin-inline: auto;
  padding:
    calc(var(--tg-safe-area-inset-top, 0px) + var(--tg-content-safe-area-inset-top, 0px))
    calc(var(--tg-safe-area-inset-right, 0px) + var(--tg-content-safe-area-inset-right, 0px))
    calc(var(--tg-safe-area-inset-bottom, 0px) + var(--tg-content-safe-area-inset-bottom, 0px) + var(--s12))
    calc(var(--tg-safe-area-inset-left, 0px) + var(--tg-content-safe-area-inset-left, 0px));
  animation: t-enter 140ms var(--ease) backwards;
}
@keyframes t-enter { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.screen[hidden] { display: none; }

/* The state switch: AM.setState() marks the blocks whose data-when carries the
   current state, so a screen may use any state name without a CSS edit. */
[data-state] > [data-when] { display: none; }
[data-state] > [data-when][data-on] { display: block; }

/* --- type roles -------------------------------------------------------------- */

.title {
  font-size: 28px; font-weight: 700; line-height: 1.16; letter-spacing: var(--f-title-track);
  padding: var(--s6) var(--s4) var(--s3);
  /* At 200 % text a long word breaks with a hyphen where Russian allows it
     (the pages are lang="ru"), never mid-syllable. */
  hyphens: auto; -webkit-hyphens: auto; overflow-wrap: break-word;
}
.caption { font-size: 13px; line-height: 1.35; color: var(--hint); }
/* A gate opened because the server did not find the agreement the session
   thought it had: one line under the title says why it is asked again. */
.gate-why { padding: 0 var(--s4) var(--s2); }

/* --- sections ---------------------------------------------------------------- */

.sec { margin-top: var(--sec-gap); }
.sec:first-child { margin-top: var(--s4); }
.sec-head {
  display: block;
  font-size: 13px; font-weight: 400; line-height: 1.3; letter-spacing: .2px;
  text-transform: uppercase; color: var(--section-header);
  padding: 0 var(--s4) 6px; margin: 0 var(--sec-inset);
}
.sec-body {
  background: var(--cell);
  border-radius: var(--sec-radius);
  margin: 0 var(--sec-inset);
  overflow: hidden;              /* clips the pressed fill at the rounded ends */
}
.sec-foot {
  font-size: 13px; line-height: 1.35; color: var(--hint);
  padding: 7px var(--s4) 0; margin: 0 var(--sec-inset);
  max-width: 36em;
}
/* Two footers under one block — the reason a button is off, then the standing
   note — read as one grey paragraph without a step between them. */
.sec-foot:not([hidden]) + .sec-foot { padding-top: 13px; }
.sec-foot.is-error { color: var(--destructive); }
/* A step caption stands alone above a form section. */
.step {
  font-size: 13px; font-weight: 400; line-height: 1.3; letter-spacing: .2px;
  text-transform: uppercase; color: var(--section-header);
  padding: 0 var(--s4) 6px; margin: var(--sec-gap) var(--sec-inset) 0;
}
/* Android draws the section header on the block itself, in the theme's own
   (accent-tinted) header colour, sentence case. */
.plat-android .sec-head {
  font-size: 15px; font-weight: 500; letter-spacing: 0; text-transform: none;
  background: var(--cell); padding: 13px var(--s4) 5px;
}
.plat-android .step { font-size: 15px; font-weight: 500; letter-spacing: 0; text-transform: none; }
/* A header that belongs to no block — above the search field, above the
   example dialogue — stays on the page on both platforms. */
.plat-android .sec.is-bare .sec-head { background: none; padding: 0 var(--s4) 6px; }

/* The situation sentence that introduces a topic on the root: the person's own
   words, one step louder than a section header, still a list header and not a
   headline — 2 lines at most, sentence case, no accent colour. */
.sec-head.is-situation {
  font-size: 17px; font-weight: 600; line-height: 1.28; letter-spacing: -.2px;
  text-transform: none; color: var(--text);
  padding-bottom: var(--s2);
}
/* The line breaks before it runs the full width of a tablet or desktop column;
   on Android the header is painted on the block, so the limit is on the text
   only — a narrowed background would cut the block in half. */
.plat-ios .sec-head.is-situation { max-width: 22em; }
.plat-android .sec-head.is-situation {
  font-size: 15px; font-weight: 500; letter-spacing: 0; color: var(--section-header);
}
.sec-head.is-situation em { font-style: normal; }

/* --- cells --------------------------------------------------------------------
   A cell is a row of the block: 44 one line, 60 with a 40 avatar and two lines,
   72 with a 54 avatar and three. The separator is inset to where the text
   starts and belongs to the row, so a row with a trailing pill still carries it. */

.sec-body > * { position: relative; }
.sec-body > *:not(:last-child)::after {
  content: ''; position: absolute; left: var(--sep-inset, var(--s4)); right: 0; bottom: 0;
  height: .5px; background: var(--separator);
}

.cell {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; min-height: 44px; padding: 8px var(--s4);
  color: var(--text);
}
.cell:active { background: var(--cell-press); }
/* A cell that is a link reads as a row, like every other cell, not as underlined text. */
a.cell { text-decoration: none; }
.cell[disabled], .cell.is-static { background: none; }
/* A row whose real control is an .sr-only input carries that input's focus. */
.cell:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
.cell-text { flex: 1 1 auto; min-width: 0; }
.cell-text > * { display: block; }
.cell-title { font-size: var(--f-body); font-weight: 400; line-height: 1.3; }
.cell-name { font-size: var(--f-body); font-weight: 600; line-height: 1.3; }
/* A list row keeps its second and third line on one line each, the way the
   chat list does; the paragraphs that must wrap ask for it with .is-wrap. */
.cell-sub, .cell-status { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cell-sub.is-wrap, .cell-status.is-wrap { white-space: normal; overflow: visible; }
.cell-sub { font-size: 15px; line-height: 1.28; color: var(--subtitle); margin-top: 1px; }
.cell-label { font-size: 13px; line-height: 1.3; color: var(--hint); margin-top: 2px; }
/* The freshness line — the slot where Telegram shows presence. Live bases take
   the wax ink, never the theme accent: an assistant is not "online". */
.cell-status { font-size: 15px; line-height: 1.28; color: var(--hint); margin-top: 1px; }
/* The ring carries the wax colour; the words stay in the text colour so that the only
   red text on a screen is Telegram's destructive one. */
.cell-status.is-live { color: var(--text); font-weight: 500; }
.cell-value {
  flex: 0 0 auto; font-size: var(--f-body); color: var(--hint);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.cell-value.is-strong { color: var(--text); font-weight: 600; }
.cell-value.is-live { color: var(--text); font-weight: 500; }
.cell.is-accent .cell-title { color: var(--accent); }
.cell.is-destructive .cell-title { color: var(--destructive); }
/* An option that stays readable but is out of play (already owned, unavailable). */
.cell.is-off .cell-title, .cell.is-off .cell-value { color: var(--hint); }
.cell.is-two { min-height: 60px; }
.cell.is-tall { min-height: 72px; }
/* A field whose value was refused: the ring is inside the cell, so it does not
   move the row. */
.cell.is-invalid { box-shadow: inset 0 0 0 2px var(--destructive); }

/* Where the separator starts: the text, not the screen edge. */
.has-av-40 { --sep-inset: 68px; }
.has-av-54 { --sep-inset: 82px; }
.has-tile  { --sep-inset: 57px; }
.has-check { --sep-inset: 50px; }
.has-mark  { --sep-inset: 45px; }

/* --- avatars and the freshness ring ---------------------------------------------
   Geometry from «Свои люди»: a dated base is a ring with one notch at the top
   right; a live base is a faint closed track with a bead travelling round it.
   The stroke is set per size so it reads at a steady ~3.1 px, and the portrait
   is inset a tenth so the ring never sits on the face. */

.avatar { position: relative; display: block; flex: 0 0 auto; width: var(--av); height: var(--av); }
/* --ring-w is the stroke in the ring's own 100-unit box, --pt-in the portrait's
   inset: both are set per size so the rim stays ~2.4 px and the gap to the face
   ~2 px, whatever the avatar measures. */
.avatar.s-28  { --av: 28px;  --ring-w: 0;   --pt-in: 0; }
.avatar.s-40  { --av: 40px;  --ring-w: 6;   --pt-in: 10%; }
.avatar.s-54  { --av: 54px;  --ring-w: 4.4; --pt-in: 9%; }
.avatar.s-72  { --av: 72px;  --ring-w: 3.3; --pt-in: 7%; }
.avatar.s-100 { --av: 100px; --ring-w: 2.6; --pt-in: 6%; }

.ring { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ring circle { fill: none; stroke-linecap: round; }
.ring-arc { stroke: var(--wax-ring); stroke-width: var(--ring-w); }
/* «база на дату» — the ring is broken open at the top right and never moves. */
.is-dated .ring-arc { stroke-dasharray: 269.3 26.1; transform: rotate(-22deg); transform-origin: 50% 50%; }
/* «обновляется» — a faint closed track, so the bead running along it reads. */
.is-live .ring-arc { stroke: var(--wax-track); }
.ring .ring-dot { fill: var(--wax); stroke: none; }
.is-live .ring-orbit { transform-origin: 50% 50%; animation: t-orbit 8s linear infinite; }
@keyframes t-orbit { to { transform: rotate(360deg); } }
.is-hero .ring-arc { animation: t-draw 240ms var(--ease) backwards; animation-delay: 120ms; }
@keyframes t-draw {
  from { opacity: 0; transform: rotate(-112deg) scale(.88); }
  to   { opacity: 1; transform: rotate(-22deg) scale(1); }
}

.portrait { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* Only a ringed avatar gives up part of its box; a bare one fills it, so the
   chat lane's 28 keeps Telegram's size. */
.avatar.is-ringed .portrait {
  inset: var(--pt-in); width: auto; height: auto;
}
.pt-bg { fill: var(--pt-bg); }
.pt-fig { fill: var(--pt-fig); }
.pt-hair { fill: var(--pt-hair); }
.pt-cut { fill: var(--pt-bg); }
.pt-seam { fill: none; stroke: var(--pt-bg); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pt-line { fill: none; stroke: var(--pt-hair); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.pt-edge { fill: none; stroke: none; }
/* T is still: Telegram's own lists do not breathe. Nothing at 28 / 40 / 54 ever
   moves — a row of restless faces competes with the one signal this product
   owns, and dozens of them at once is the classic mid-range-Android list
   failure. */
.pt-body, .pt-head { animation: none; }

/* --- the portrait's motion: a hero device only -----------------------------------
   A portrait carries at most one preset, declared by the renderer as
   `data-motion` on the SVG. It plays at 100 px — the profile head, the portrait
   maker's hero, the owner's view of an application — and on an avatar that asks
   for it by name (`AM.t.avatar(p, size, {motion:true})`), which exists only as
   the preview beside each preset in the portrait maker. Transform only, on the
   portrait's own groups, so nothing outside the disc is laid out again.
   The rotations pivot on the neck in the SVG's own coordinates rather than on
   the group's bounding box: an accessory changes that box, and the head would
   then turn about a different point depending on whether it wears earrings. */

.avatar.s-100 [data-motion="breath"] .pt-body,
.avatar.is-animated [data-motion="breath"] .pt-body {
  animation: t-breath var(--breath, 6s) ease-in-out infinite var(--breath-delay, 0s);
}
@keyframes t-breath { 0%, 100% { transform: translateY(1.1px); } 50% { transform: translateY(-1.1px); } }

.avatar.s-100 [data-motion="nod"] .pt-head,
.avatar.is-animated [data-motion="nod"] .pt-head {
  transform-box: view-box; transform-origin: 32px 40px;
  animation: t-nod 11s ease-in-out infinite var(--tilt-delay, 0s);
}
@keyframes t-nod { 0%, 58%, 100% { transform: rotate(0deg); } 74% { transform: rotate(2.6deg); } }

.avatar.s-100 [data-motion="look"] .pt-head,
.avatar.is-animated [data-motion="look"] .pt-head {
  transform-box: view-box; transform-origin: 32px 40px;
  animation: t-look 9s ease-in-out infinite var(--tilt-delay, 0s);
}
@keyframes t-look {
  0%, 22%, 100% { transform: translateX(0) scaleX(1); }
  44%, 62%      { transform: translateX(-1.8px) scaleX(.94); }
}

/* The 17 px mark beside a date, a purchase option or the chat's pinned strip. */
.mark-mini { flex: 0 0 auto; width: 17px; height: 17px; overflow: visible; }
.mark-mini circle { fill: none; stroke: var(--wax-ring); stroke-width: 13; stroke-linecap: round; }
/* The notch is opened wider than on the avatars: at 17 px a 30° gap disappears. */
.mark-mini.is-dated circle { stroke-dasharray: 235.3 38; transform: rotate(-26deg); transform-origin: 50% 50%; }
.mark-mini.is-live circle { stroke: var(--wax-track); }

/* --- icons and tiles ---------------------------------------------------------- */

.ic {
  flex: 0 0 auto; width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.chev {
  width: 13px; height: 13px; stroke-width: 2.6; color: var(--separator);
  margin-left: calc(var(--s1) * -1);
}
.plat-android .chev { display: none; }

.tile {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 29px; height: 29px; border-radius: var(--r-tile); color: #ffffff;
}
.tile .ic { width: 18px; height: 18px; stroke-width: 2; }
.tile-blue { background: var(--tile-blue); }
.tile-green { background: var(--tile-green); }
.tile-orange { background: var(--tile-orange); }
.tile-red { background: var(--tile-red); }
.tile-purple { background: var(--tile-purple); }
.tile-gray { background: var(--tile-gray); }

/* --- the pill: a real control, never decoration -------------------------------
   A row that carries one lives in `.row`, so the pill is a sibling of the row
   button and never a control inside a control. */

.row { display: flex; align-items: center; padding-right: var(--s4); }
/* min-width:0 is what lets a long subject give way to the pill instead of
   pushing it off the row. */
.row > .cell { flex: 1 1 auto; min-width: 0; padding-right: 0; }

.pill-hit {
  display: inline-flex; align-items: center; justify-content: flex-end;
  flex: 0 0 auto; min-height: 44px; min-width: 44px; padding-left: var(--s2);
}
.pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 30px; padding: 0 12px; border-radius: var(--r-pill);
  background: var(--pill-fill); color: var(--accent);
  font-size: 15px; font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Owned: the filled pill of Telegram's own app list. */
.pill.is-filled {
  background: var(--button); color: var(--on-button);
  font-size: 13px; letter-spacing: .3px; text-transform: uppercase;
}
.pill-hit:active .pill { opacity: .55; }

/* --- folder-style tabs --------------------------------------------------------- */

.tabs {
  display: flex; gap: 0; overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  background: var(--cell); box-shadow: inset 0 -.5px 0 var(--separator);
  position: sticky; top: 0; z-index: 2;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: 0 0 auto; position: relative; min-height: 44px; padding: 0 var(--s4);
  display: inline-flex; align-items: center;
  font-size: 15px; font-weight: 500; white-space: nowrap; color: var(--hint);
}
.tab[aria-pressed="true"] { color: var(--accent); font-weight: 600; }
.tab[aria-pressed="true"]::after {
  content: ''; position: absolute; left: var(--s4); right: var(--s4); bottom: 0;
  height: 3px; border-radius: 3px 3px 0 0; background: var(--accent);
}
.tab:active { opacity: .6; }

/* --- the search field ----------------------------------------------------------- */

.field {
  display: flex; align-items: center; gap: var(--s2);
  background: var(--field); border-radius: var(--r-field);
  margin: 0 calc(var(--sec-inset) + var(--s4));
  padding: 0 var(--s3); min-height: 44px;
}
.plat-android .field { margin: 0 var(--s4); }
.field .ic { width: 18px; height: 18px; color: var(--hint); }
.field input {
  flex: 1 1 auto; min-width: 0; background: none; border: 0; outline: none;
  font-size: 17px; color: var(--text); padding: 11px 0;
}
/* The example question is long on purpose, so the placeholder is set two steps
   down to survive 360 px; the field itself stays at 17 px, and the label above
   it — not the placeholder — is what names the field. */
.field input::placeholder { color: var(--hint); font-size: 13px; }
.field input::-webkit-search-cancel-button { display: none; }

/* --- form cells: Telegram's edit-profile manner -----------------------------------
   The field lives inside a section; its visible label is the section header
   above it, or the inline label at the head of the row. */

.fld {
  flex: 1 1 auto; min-width: 0; margin: 0; padding: 11px 0;
  background: none; border: 0; outline: none; resize: none;
  font-family: var(--ui); font-size: var(--f-body); line-height: 1.32;
  color: var(--text); min-height: 44px;
}
.fld::placeholder { color: var(--hint); }
.fld:disabled { color: var(--hint); -webkit-text-fill-color: var(--hint); opacity: 1; }
textarea.fld { max-height: 180px; overflow-y: auto; }
.fld-label { flex: 0 0 auto; font-size: var(--f-body); color: var(--text); }
.fld-count {
  flex: 0 0 auto; align-self: center; font-size: 13px; color: var(--hint);
  font-variant-numeric: tabular-nums;
}
.fld-count.is-over { color: var(--destructive); }

/* A native select dressed as a value cell: the chevron and the value are ours,
   the wheel is the platform's. */
.cell.is-select { position: relative; }
.select-native {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; border: 0; background: none;
  font-size: 16px;               /* iOS zooms the page under 16 px */
}

/* --- the switch: a real checkbox, the platform's pill ------------------------------ */

/* The whole row is the label, so the row is the target; the input still covers
   a full 44 px itself, for anyone who taps the switch alone. */
.switch { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; min-height: 44px; }
.switch input {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0;
}
/* The knob lives inside the track, so it is centred on the track and not on
   the 44 px target around it. */
.switch-track {
  position: relative; display: block; width: 51px; height: 31px; border-radius: 999px;
  background: var(--switch-off); transition: background 160ms var(--ease);
}
.switch-knob {
  position: absolute; left: 2px; top: 2px; width: 27px; height: 27px; border-radius: 50%;
  background: #ffffff; box-shadow: 0 1px 2px rgba(0, 0, 0, .28);
  transition: transform 160ms var(--ease);
}
.switch:has(input:checked) .switch-track { background: var(--button); }
.switch:has(input:checked) .switch-knob { transform: translateX(20px); }
.switch:has(input:focus-visible) .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch:has(input:disabled) { opacity: .5; }
/* Android's switch is a thin track with a knob that grows when it is on. */
.plat-android .switch-track { width: 44px; height: 26px; }
.plat-android .switch-knob {
  left: 5px; top: 7px; width: 12px; height: 12px; box-shadow: none;
  background: var(--cell); transition: transform 160ms var(--ease), width 160ms var(--ease),
    height 160ms var(--ease), left 160ms var(--ease), top 160ms var(--ease);
}
.plat-android .switch:has(input:checked) .switch-knob {
  left: 3px; top: 3px; width: 20px; height: 20px; transform: translateX(18px);
}

/* --- choice marks: the round check and the trailing checkmark ------------------------ */

.check {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  border: 1.6px solid var(--control-border); color: transparent;
}
.check .ic { width: 14px; height: 14px; stroke-width: 2.6; }
.check-hit { position: relative; }
/* Both forms work: a real checkbox inside the row, or aria-checked on the row. */
[aria-checked="true"] .check,
.cell:has(input:checked) .check,
label:has(input:checked) .check {
  background: var(--accent); border-color: var(--accent); color: var(--on-button);
}
/* The trailing checkmark of a chosen option keeps its slot when it is off, so a
   column of prices does not shift as the choice moves. */
.tick { color: var(--accent); }
.tick[data-off] { visibility: hidden; }

/* --- pickers: swatches and silhouettes as one radio group --------------------------- */

.picks { display: flex; flex-wrap: wrap; gap: var(--s3); padding: var(--s3) var(--s4); }
.pick { position: relative; display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; }
.pick input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; }
.pick-face { display: block; border-radius: 50%; }
.pick-dot { width: 36px; height: 36px; background: var(--pick-tone, var(--cell-press)); }
.pick:has(input:checked) .pick-face { box-shadow: 0 0 0 2px var(--cell), 0 0 0 4px var(--accent); }
.pick:has(input:focus-visible) .pick-face { box-shadow: 0 0 0 2px var(--cell), 0 0 0 4px var(--accent); }

/* --- the composer: our own control, never a copy of Telegram's ----------------------- */

.composer { display: flex; align-items: flex-end; gap: var(--s2); padding: var(--s2) var(--s3); background: var(--plain-page); }
/* The field takes what is left and may shrink below its text; the send button
   never shrinks, so at 200 % text it stays on screen and tappable. */
.composer .field { flex: 1 1 0; min-width: 0; margin: 0; border-radius: 18px; align-items: center; padding: 0 var(--s3); }
.composer .fld { min-width: 0; width: 100%; padding: 11px 0; max-height: 108px; }
.composer .fld::placeholder { font-size: 13px; }
/* The line above the field is its visible label and carries the trial count. */
.ask-label { display: block; padding: 2px var(--s6) 4px; font-size: 13px; line-height: 1.3; color: var(--hint); }
.send {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--button); color: var(--on-button);
}
.send:active { opacity: .7; }
.send:disabled { opacity: .4; }

/* --- bubbles ---------------------------------------------------------------------
   Allowed in T because Telegram asks an app to mimic the components that exist;
   what is not allowed is the frame around them — no fake header, no read ticks,
   no presence, no attach or microphone controls. */

.thread { display: flex; flex-direction: column; gap: 2px; padding: var(--s3) var(--s4); background: var(--plain-page); }

/* A few bubbles inside a grouped list — a moderator's comment to an author, the
   thread between the owner and an author: the chat surface as its own block,
   the way the profile shows an example dialogue. Never a control. */
.talk { margin: 0 var(--sec-inset); border-radius: var(--sec-radius); overflow: hidden; background: var(--plain-page); }
.talk .thread { padding: var(--s3); }
.talk .bubble { font-size: 15px; }

.bubble {
  position: relative; max-width: 82%; padding: 7px 12px 8px;
  border-radius: var(--r-bubble); font-size: var(--f-body); line-height: 1.32;
}
.bubble.is-in { align-self: flex-start; background: var(--bubble-in); color: var(--text); }
.bubble.is-out { align-self: flex-end; background: var(--bubble-out); color: var(--on-button); }
/* The last bubble of a group tightens one corner — Telegram's tail, without
   drawing Telegram's tail. */
.bubble.is-in.is-last { border-bottom-left-radius: 6px; }
.bubble.is-out.is-last { border-bottom-right-radius: 6px; }
.bubble-row { display: flex; align-items: flex-end; gap: var(--s2); }
.bubble-row .bubble { max-width: calc(82% - 36px); }

/* the answer types itself out (AM.typer) */
.w { white-space: nowrap; }
.ch { opacity: 0; }
.ch.on { opacity: 1; }
.caret {
  display: inline-block; width: 2px; height: 1em; margin-left: 1px; vertical-align: -.14em;
  background: currentColor; animation: t-blip 1s steps(1) infinite;
}
@keyframes t-blip { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* --- the service block: Telegram's centred, rounded, in-thread note -----------------
   It sits on the bubble material: --hint on a translucent fill falls under
   4.5:1 in three of the four default themes. */

.service {
  align-self: center; max-width: 88%; margin: var(--s2) auto;
  padding: 6px 12px; border-radius: 14px;
  background: var(--bubble-in); color: var(--hint);
  font-size: 13px; line-height: 1.35; text-align: center;
}

/* --- inline-keyboard-style buttons under a message ------------------------------------ */

/* Side by side while both labels fit; under large text they wrap to a stack. */
.inline-row { display: flex; flex-wrap: wrap; gap: 2px; max-width: 82%; margin-top: 2px; }
.inline-row.is-stack { flex-direction: column; }
.inline-btn {
  flex: 1 1 0; min-width: min(100%, max-content); min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  padding: 6px var(--s2); border-radius: 12px;
  background: var(--bubble-in); color: var(--accent);
  font-size: 14px; font-weight: 500; text-align: center;
}
.inline-btn.is-destructive { color: var(--destructive); }
.inline-btn:active { opacity: .6; }

/* --- the card: the app speaking inside a thread, never a bubble, never a dialog -------- */

.card { position: relative; background: var(--bubble-in); border-radius: var(--r-sec); overflow: hidden; }
.card.is-alarm::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--destructive);
}
.card-body { padding: var(--s3) var(--s4) var(--s3) calc(var(--s4) + 3px); }
.card-t { font-size: 15px; font-weight: 600; line-height: 1.3; color: var(--destructive); }
.card-p { font-size: 15px; line-height: 1.38; color: var(--text); max-width: 34em; }
.card-t + .card-p { margin-top: var(--s1); }
/* The emergency numbers close the card: large, one per line, selectable. */
.card-nums { margin-top: var(--s2); }
.card-num-row { display: block; font-size: 15px; line-height: 1.5; color: var(--text); }
.card-num {
  font-size: 24px; font-weight: 700; line-height: 1.25; letter-spacing: .01em;
  font-variant-numeric: tabular-nums; color: var(--text); user-select: all; -webkit-user-select: all;
}
.card-live:empty { display: none; }
.card-live {
  padding: 0 var(--s4) var(--s3) calc(var(--s4) + 3px);
  font-size: 13px; line-height: 1.35; color: var(--hint);
}
.card-act {
  display: flex; align-items: center; justify-content: center; width: 100%; min-height: 44px;
  box-shadow: inset 0 .5px 0 var(--separator);
  font-size: 15px; font-weight: 500; color: var(--accent);
}
.card-act.is-destructive { color: var(--destructive); }
.card-act:active { background: var(--cell-press); }
/* Handing the reader over to a specialist: a contact card in the assistant's
   own lane, not a bubble with a button inside it. */
.card-person {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 56px; padding: var(--s2) var(--s3); border-radius: var(--r-bubble);
}
.card-person:active { opacity: .7; }
.card-person .cell-text { flex: 1 1 auto; min-width: 0; }

/* --- the pinned identity strip (Telegram's pinned-message bar) -------------------------- */

.pinned {
  display: flex; align-items: center; gap: var(--s3); width: 100%;
  min-height: 44px; padding: 6px var(--s4); background: var(--plain-page);
  box-shadow: inset 0 -.5px 0 var(--separator);
}
/* Telegram's pinned bar is the theme's accent; the wax beside it is the
   freshness mark and means something else. */
.pinned::before {
  content: ''; flex: 0 0 auto; align-self: stretch; width: 2px; border-radius: 2px;
  background: var(--accent); margin: 2px 0;
}
.pinned .cell-text { flex: 1 1 auto; min-width: 0; }
.pinned .pin-1, .pinned .pin-2 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pinned .pin-1 { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--text); }
.pinned .pin-2 { font-size: 13px; line-height: 1.3; color: var(--hint); }
.pinned .mark-mini { align-self: flex-start; margin-top: 3px; }
/* Keyboard open: the strip is one line, and the fact that answers are written
   by an AI moves to the end of that line instead of being scrolled away. */
.pinned .pin-ai { display: none; flex: 0 0 auto; font-size: 13px; color: var(--hint); }
.pinned.is-collapsed .pin-2 { display: none; }
.pinned.is-collapsed .pin-ai { display: block; }
.pinned:active { background: var(--cell-press); }

/* --- an outcome: one face, one sentence, then what happened ------------------------------ */

.out-head { text-align: center; padding: var(--s8) var(--s4) var(--s2); }
.out-head .avatar { margin: 0 auto var(--s3); }
.out-title { font-size: 22px; font-weight: 600; line-height: 1.2; }
.out-lede { margin: var(--s2) auto 0; max-width: 30em; font-size: 15px; line-height: 1.4; color: var(--hint); }

/* --- an inline notice: empty, error, offline ---------------------------------------------- */

.notice { padding: var(--s12) var(--s8) 0; text-align: center; }
.notice h2 { font-size: 17px; font-weight: 600; line-height: 1.3; color: var(--text); }
.notice p { margin-top: var(--s2); font-size: 15px; line-height: 1.4; color: var(--hint); }
.text-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 0 var(--s3); color: var(--accent);
  font-size: var(--f-body); font-weight: 500;
}
.text-btn:active { opacity: .55; }
.text-btn.is-destructive { color: var(--destructive); }

/* --- skeletons ------------------------------------------------------------------------- */

.sk { background: var(--skeleton); border-radius: 6px; }
.sk-on-cell { background: var(--skeleton-on-cell); }
.sk-circle { border-radius: 50%; }
.sk-pulse { animation: t-pulse 1.4s ease-in-out infinite; }
@keyframes t-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }

/* --- reduced motion ---------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .screen, .sk-pulse, .caret, .is-live .ring-orbit, .is-hero .ring-arc,
  .pt-body, .pt-head { animation: none !important; }
  /* The presets' first frame is the neutral pose, so a stopped portrait is the
     same drawing a still portrait is. */
  .pt-body, .pt-head { transform: none !important; }
  .switch-track, .switch-knob { transition: none !important; }
  .ch { opacity: 1 !important; }
  .caret { display: none; }
}

/* ===== the owner's area ======================================================
   Prefix: `a-`. What lives here is exactly the seven components the owner's
   screens share — the period strip, the overview grid, the statement, share
   rows, the bar chart, the resolution stamp and the case row — plus the amount
   head of a receipt and two small parts (a dismissible filter line, a monogram
   for a helper with no portrait yet).

   The one colour of the product — sealing wax — is NOT used here. It means
   knowledge freshness, and the only place it appears in the owner's area is the
   ring around an assistant's portrait, exactly as everywhere else. Bars, tabs,
   checkmarks and selections take the theme's accent; refunds and removals take
   Telegram's destructive.
   =========================================================================== */

/* --- the period strip -------------------------------------------------------
   Seven tabs on one line that scrolls inside itself, with the exact range
   spelled underneath, so «Месяц» never has to be guessed at. The strip and the
   range travel together: the range is the half that answers the question.

   The block sticks to the page, not to the <div> it was mounted into:
   mountPeriodTabs() takes that host out of the box tree (`display: contents`),
   because a sticky box only sticks inside its own parent and a host exactly as
   tall as the strip is a cage. No screen needs a rule of its own for it. */

.a-periods {
  position: sticky; top: 0; z-index: 2;
  background: var(--cell);
  box-shadow: inset 0 -.5px 0 var(--separator);
}
.a-periods .tabs { position: static; box-shadow: none; }
.a-range {
  padding: 0 var(--s4) 7px;
  font-size: 13px; line-height: 1.3; color: var(--hint);
  font-variant-numeric: tabular-nums;
}
/* On a desktop or tablet window there is no touch drag, so a tab past the
   column edge would be unreachable; the strip wraps instead. */
@media (min-width: 520px) {
  .a-periods .tabs { flex-wrap: wrap; overflow-x: visible; }
}
/* On a window wider than the column's own width the strip keeps the column's
   inset, like the blocks under it, instead of running edge to edge. */
@media (min-width: 600px) {
  .a-periods { margin-inline: var(--s4); border-radius: var(--r-sec); }
  /* The seven tabs share the narrower row instead of wrapping one onto a second line. */
  .a-periods .tab { flex: 1 1 auto; justify-content: center; padding: 0 var(--s2); }
  .a-periods .tab[aria-pressed="true"]::after { left: var(--s2); right: var(--s2); }
}

/* --- the overview grid ------------------------------------------------------
   Telegram's statistics block: a big tabular number, its name under it, and the
   change against the previous equal period in words. No arrows, no colour for
   direction — «на 12 % больше» says the direction better than a green triangle,
   and it still says it to somebody who cannot see the colour. */

/* Two columns while two columns fit, one when they do not — and «fit» is
   measured in the block's own type, not in a device width. A column narrower
   than about nine letters of the reader's font cannot hold «12 450 ⭐» and the
   word under it, so at 200 % text the four figures fall into one column by the
   same rule that already gives one column to a 320 px phone. */
.a-grid {
  /* At most two columns (a column is never under a third of the row), so four
     figures on a wide window are two even rows, never three and one. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, max(9em, 34%)), 1fr));
  gap: var(--s4) var(--s3); padding: var(--s4);
}
.a-num {
  font-size: 24px; font-weight: 600; line-height: 1.12; letter-spacing: -.3px;
  font-variant-numeric: tabular-nums;
}
.a-label { margin-top: 3px; font-size: 15px; line-height: 1.25; color: var(--subtitle); }
.a-delta { margin-top: 2px; font-size: 13px; line-height: 1.32; color: var(--hint); }
/* ⭐ is the sign of the unit, not a digit: at 24 px it reads as a sticker beside
   the number, so it is set one size down wherever an amount is a headline. The
   floor is the area's own minimum: beside a 16 px value (a share row, an
   assistant's measure) .72em would drop the sign to 11.5 px. */
.a-unit { font-size: max(12px, .72em); }

/* --- the statement ----------------------------------------------------------
   A day band carrying that day's total, then that day's rows. The rows add up
   to the band: a refunded purchase stays in the day it was paid, struck
   through, because that is how the money actually moved. */

.a-day .cell { min-height: 32px; padding-top: 11px; padding-bottom: 5px; }
.a-day .cell-title {
  font-size: 13px; font-weight: 400; letter-spacing: .2px; text-transform: uppercase;
  color: var(--section-header);
}
.a-day .cell-value { font-size: 13px; }
/* Android writes its list headers in sentence case at 15/500 — the day band
   follows the manner of the platform it is drawn on. */
.plat-android .a-day .cell-title {
  font-size: 15px; font-weight: 500; letter-spacing: 0; text-transform: none;
}
.plat-android .a-day .cell-value { font-size: 15px; }

.a-struck { text-decoration: line-through; }
.a-bad { color: var(--destructive); }

/* --- share rows -------------------------------------------------------------
   One row per source: the name, the amount as the right-hand value, and a thin
   proportional bar under both. Direct labels instead of a pie — six slices of a
   circle cannot be read at 360 px, and six rows can. */

.a-share .cell-text { min-width: 0; }
.a-share-head { display: flex; align-items: baseline; gap: var(--s2); }
/* The label wraps instead of ellipsing: a truncated source name («С обновлениями
   (первые опла…») is a row nobody can read, and the bar sits below it anyway. */
.a-share-label {
  flex: 1 1 auto; min-width: 0; font-size: var(--f-body); line-height: 1.3;
}
.a-share-value {
  flex: 0 0 auto; font-size: var(--f-body); font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.a-track {
  display: block; height: 4px; margin-top: 7px; border-radius: 2px;
  background: var(--pill-fill); overflow: hidden;
}
/* Square where it starts, rounded where it ends: the bar grows from a baseline.
   A minimum width keeps a real-but-tiny share visible instead of invisible — and
   for exactly that reason a share of nothing is drawn as no fill at all
   (shareRows() leaves the element out), or the minimum would paint a stub on a
   source that brought no money. */
.a-fill {
  display: block; height: 100%; min-width: 3px;
  border-radius: 0 2px 2px 0; background: var(--accent);
}
.a-note { display: block; margin-top: 4px; font-size: 13px; line-height: 1.3; color: var(--hint); }
.a-note.is-bad { color: var(--destructive); }

/* --- the bar chart ----------------------------------------------------------
   One series, the theme's accent, no gridlines and no legend: with a single
   series the section header already says what is plotted. Values are written on
   the tallest column and the last one only — a number over every column is
   noise — and every value is also in the table twin below, for a reader who
   hears the screen instead of seeing it. */

.a-chart { padding: var(--s3) var(--s4) var(--s2); }
.a-chart-svg { display: block; width: 100%; height: auto; }
.a-bar { fill: var(--accent); }
/* A bucket with nothing in it keeps its place as a hairline stub, so an empty
   Tuesday reads as an empty Tuesday and not as a missing one. The separator
   tone, not the accent tint: the tint is invisible on a light cell. */
.a-bar-zero { fill: var(--separator); }
.a-axis { stroke: var(--separator); stroke-width: 1; }
.a-chart-val {
  font-size: 12px; font-weight: 600; fill: var(--text);
  font-variant-numeric: tabular-nums;
}
.a-chart-x { font-size: 12px; fill: var(--hint); font-variant-numeric: tabular-nums; }
.a-chart-empty { font-size: 15px; line-height: 1.4; color: var(--hint); padding: var(--s2) 0; }
.a-chart-single {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  font-size: var(--f-body); padding: var(--s2) 0;
}
.a-chart-single .num { font-weight: 600; font-variant-numeric: tabular-nums; }

/* --- the resolution stamp ---------------------------------------------------
   Every moderation case and every refund ends in a word and a time. It sits
   above the sections it explains, the way a stamp sits on top of a document. */

.a-stamp {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) var(--s4) 0; margin: 0 var(--sec-inset);
  font-size: 15px; font-weight: 500; line-height: 1.3; color: var(--hint);
}
.a-stamp .ic { width: 18px; height: 18px; }
.a-stamp.is-bad { color: var(--destructive); }
/* Under a centred head — a receipt's amount, an application's portrait — the
   stamp centres with it. `stamp(word, at, tone, true)` asks for this; everywhere
   else the stamp keeps the section's own left edge. */
.a-stamp.is-head { justify-content: center; padding-top: var(--s2); }

/* --- the case row -----------------------------------------------------------
   A queue item: who it is about, who sent it, how long it has waited, and any
   line that makes it urgent. The extra lines make the row taller than 72 — the
   cell height is a minimum, not a cage. */

.a-case { align-items: flex-start; padding-top: 10px; padding-bottom: 10px; }
.a-case .avatar { margin-top: 2px; }
.a-case .chev { align-self: center; }
/* The second line carries the subject AND the author's handle. A chat-list row
   would clip it; a queue row must not — «@i…» names nobody. The status line
   wraps for the same reason: «Одобрена, ждёт оплаты · 13 сентя…» hides the half
   the owner opened the queue for. */
.a-case .cell-sub,
.a-case .cell-status { white-space: normal; overflow: visible; }

/* The stand-in for a helper with no portrait yet. It fills the avatar slot it
   stands in — 40 in a queue row, 54 in a topic row, 100 at the head of an
   application — or the row's separator would start off its face. */
.a-mono {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--pill-fill); color: var(--accent);
  font-size: 17px; font-weight: 600; line-height: 1;
}
.a-mono.is-54 { width: 54px; height: 54px; font-size: 22px; }
.a-mono.is-100 { width: 100px; height: 100px; font-size: 40px; }

/* --- the receipt head -------------------------------------------------------
   The amount is the headline of a purchase; the product and the moment are what
   it was for. Proportional figures would let «1 525» drift, so the amount is
   tabular like every other amount in the area. */

.a-head { text-align: center; padding: var(--s8) var(--s4) var(--s2); }
.a-amount {
  font-size: 34px; font-weight: 700; line-height: 1.1; letter-spacing: -.6px;
  font-variant-numeric: tabular-nums;
}
.a-what { margin-top: var(--s2); font-size: var(--f-body); line-height: 1.32; }
.a-when {
  margin-top: var(--s1); font-size: 15px; line-height: 1.3; color: var(--hint);
  font-variant-numeric: tabular-nums;
}

/* --- small parts ------------------------------------------------------------ */

/* The line above a pre-filtered list, with the way out of the filter on it. */
.a-filter .cell-title { color: var(--accent); }
.a-x {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; color: var(--hint);
}
.a-x:active { opacity: .5; }

/* A value that is somebody's own text — a name — may wrap. Without this the
   value keeps its whole width and squeezes the label to «Им / я». */
.cell.is-freevalue .cell-text { flex: 0 0 auto; }
.cell.is-freevalue .cell-value {
  flex: 1 1 auto; min-width: 0; white-space: normal; text-align: right;
}

/* «Всё разобрано» — a section body with one quiet sentence instead of rows. */
.a-quiet {
  padding: var(--s3) var(--s4);
  font-size: var(--f-body); line-height: 1.35; color: var(--hint);
}
