:root{
  --cream:#FBF3E4; --ink:#3A2A1E; --purple:#4A3226; --pink:#C1633F; --yellow:#E9A13B; --green:#7FA362;
  --muted:#8A705C; --line:#E8DCC8; --tint:#F3E8D4; --tint-k:#E9F0DC; --white:#FFFFFF;
  --sh-pill: inset 0 1px 0 #FFFFFF, inset 0 -2px 0 rgba(58,42,30,.08), 0 1px 2px rgba(58,42,30,.05), 0 6px 16px rgba(58,42,30,.07);
  --sh-tab: inset 0 1px 0 #FFFFFF, inset 0 -2px 0 rgba(58,42,30,.06), 0 2px 6px rgba(58,42,30,.05), 0 12px 28px rgba(58,42,30,.12);
  --sh-card: 0 6px 14px rgba(58,42,30,.10), 0 22px 48px rgba(58,42,30,.22);
  --sh-yellow: inset 0 2px 0 rgba(255,255,255,.45), inset 0 -3px 0 rgba(58,42,30,.24), 0 2px 6px rgba(58,42,30,.20), 0 10px 24px rgba(58,42,30,.26);
  --sh-pink: inset 0 2px 0 rgba(255,255,255,.32), inset 0 -3px 0 rgba(58,42,30,.30), 0 2px 6px rgba(58,42,30,.20), 0 10px 24px rgba(58,42,30,.26);
  --glow: radial-gradient(420px 300px at 88% -4%, rgba(193,99,63,.62), rgba(193,99,63,0) 62%);
  --serif:'Newsreader',Georgia,serif; --sans:'DM Sans',system-ui,-apple-system,sans-serif;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
body{margin:0;background:#EFE3CD;color:var(--ink);font-family:var(--sans);font-size:15px;line-height:1.45;-webkit-font-smoothing:antialiased;min-height:100vh}
/* The app fills the screen on a phone and sits as a single column on a
   desktop — the same app either way, not a device mock-up on a page. */
.page{min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center}
/* .phone is the pre-0.6.0 class name, kept as an alias so a browser holding
   a cached copy of the old index.html still gets a styled app container
   instead of a collapsed one. */
.app,.phone{position:relative;width:100%;height:100vh;height:100dvh;overflow:hidden;background:var(--cream)}
@media (min-width:760px){
  body{background:#EFE3CD}
  /* Full height, edge to edge: the app column runs the whole window with no
     letterboxing above or below — only the sides show the page behind it. */
  .page{padding:0}
  .app,.phone{max-width:480px;height:100vh;height:100dvh;
    box-shadow:0 0 60px -12px rgba(58,42,30,.30)}
  .aside{display:none}
}
/* Only surfaced when progress can't reach the server — otherwise silent. */
.netChip{position:fixed;left:50%;transform:translateX(-50%);top:calc(12px + env(safe-area-inset-top));z-index:40;
  max-width:min(420px,calc(100vw - 32px));padding:9px 16px;border-radius:999px;background:var(--white);
  box-shadow:var(--sh-pill);font-size:12.5px;font-weight:500;color:var(--muted);text-align:center}

.screen{position:absolute;inset:0;display:flex;flex-direction:column;padding:calc(20px + env(safe-area-inset-top)) 20px 24px;overflow:hidden;animation:in .28s ease both}
.screen.scroll{overflow-y:auto;padding-bottom:calc(100px + env(safe-area-inset-bottom))}
.screen.scroll.notabs{padding-bottom:calc(24px + env(safe-area-inset-bottom))}
/* A screen that doesn't scroll still has the tab bar floating over its
   bottom — it needs the same clearance, or its last element sits under it. */
.screen.hastabs:not(.scroll){padding-bottom:calc(92px + env(safe-area-inset-bottom))}
.screen.bleed{padding:0}
/* A bleed screen that scrolls: langWrap normally fills the frame exactly,
   so on a short window its last button was simply cut off. */
.screen.scroll.bleed{padding:0}
.screen.scroll .langWrap{flex:0 0 auto;min-height:100%;overflow:visible}
@keyframes in{from{opacity:0;transform:translateX(24px)}to{opacity:1;transform:none}}
.screen.back{animation:inb .28s ease both}
@keyframes inb{from{opacity:0;transform:translateX(-24px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.screen,.screen.back{animation:none}}

button{font:inherit;color:inherit;border:0;background:none;cursor:pointer;padding:0}
button:active .press,.press:active{transform:scale(.97)}
.eyebrow{font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
h1.t{font-family:var(--serif);font-weight:400;font-size:44px;line-height:1.06;letter-spacing:-.01em;margin:14px 0 0;text-wrap:pretty}
h2.t{font-family:var(--serif);font-weight:400;font-size:36px;line-height:1.08;letter-spacing:-.01em;margin:22px 0 0}
/* Player title: half the usual h2 — the exercise name is a label above the
   footage here, not the screen's headline (AK, 0.22.0). */
h2.t.ptitle{font-size:18px;line-height:1.2;margin-top:14px}
.sub{margin-top:10px;color:var(--muted);font-size:15px}
.row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.circ{width:44px;height:44px;border-radius:50%;background:var(--white);box-shadow:var(--sh-pill);display:flex;align-items:center;justify-content:center;flex:none}
.pill{display:inline-flex;align-items:center;gap:8px;height:44px;padding:0 16px;border-radius:999px;background:var(--white);box-shadow:var(--sh-pill);font-size:15px;font-weight:500;color:var(--ink)}
.pill.sm{height:40px;padding:0 12px;font-size:12px;font-weight:600}
/* flex-shrink:0 — a button sitting straight inside one of the app's many
   flex columns (.screen itself, a card's footer) otherwise gets squashed by
   the flex algorithm whenever that column's content overflows: the browser
   shrinks it below its own height to make room instead of leaving it to
   scroll. That is what made "Build my own" render ~28px tall instead of its
   set height on a tall pick-a-program screen (AK, сент. 2026). */
.btn{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;gap:10px;height:56px;padding:0 26px;border-radius:999px;font-size:17px;font-weight:600;transition:transform .12s ease,filter .12s}
.btn:active{transform:scale(.97)}
.btn.y{background:var(--yellow);color:var(--ink);box-shadow:var(--sh-yellow)}
.btn.p{background:var(--pink);color:#fff;box-shadow:var(--sh-pink)}
.btn.w{background:var(--white);color:var(--muted);box-shadow:var(--sh-pill)}
/* Outline on the purple gate — the third choice, visibly a button. */
.btn.o{background:transparent;color:var(--cream);border:1.5px solid rgba(251,243,228,.5)}
.btn.sm{height:44px;padding:0 18px;font-size:15px}
.btn.lg{height:66px;font-size:19px;padding:0 30px}
.btn[disabled]{filter:saturate(.3) opacity(.55);pointer-events:none}
.linklike{display:block;width:100%;background:none;border:0;padding:8px 0;font:inherit;font-size:13.5px;font-weight:600;color:inherit;opacity:.62;text-decoration:underline;text-align:left;cursor:pointer}
.skip{display:block;width:100%;background:none;border:0;padding:10px 0;font:inherit;font-size:14px;font-weight:600;color:inherit;opacity:.68;text-decoration:underline;text-align:center;cursor:pointer}
.card{position:relative;border-radius:28px;overflow:hidden;box-shadow:var(--sh-card)}
.card.purple{background:var(--glow),var(--purple);color:var(--cream)}
.card.green{background:var(--green);color:var(--ink)}
.rings{position:absolute;left:0;bottom:0;pointer-events:none}
/* Lives in the app shell, not in the screen — see render() in app.js.
   Only the current tab carries its label, and carries it as a filled pill in
   the mode's colour: the old pale-lavender fill barely read as "selected",
   and stacking an icon over a label on all three made the bar 72px tall. The
   tabs are centred and sized by their content rather than stretched, so a
   long word in any language just makes its own pill wider. */
/* Five seats since 0.10.3 (Guides joined). On a 390px phone the bar's inner
   width is 342px, and at the old 48px seats + 6px gaps the active pill had
   ~110px and "Library" already clipped to "Librar…". 44px seats (still the
   44px hit target the round buttons use) and 5px gaps buy back 16px. */
.tabs{position:absolute;left:20px;right:20px;bottom:calc(20px + env(safe-area-inset-bottom));z-index:5;display:flex;justify-content:center;align-items:center;gap:5px;height:56px;padding:4px;border-radius:999px;background:var(--white);box-shadow:var(--sh-tab)}
.tab{display:flex;align-items:center;justify-content:center;gap:8px;flex:none;width:44px;height:48px;border-radius:999px;color:var(--muted);font-size:13px;font-weight:700;transition:background-color .18s ease,color .18s ease}
.tab span{display:none;white-space:nowrap}
.tab.on{width:auto;min-width:0;flex:0 1 auto;padding:0 18px;background:var(--purple);color:var(--cream)}
.tab.on.k{background:var(--green);color:#05301F}
/* The active pill sizes to its own word, so a long label in some language has
   to give way rather than push the fourth control off the end of the bar. */
.tab.on span{display:block;overflow:hidden;text-overflow:ellipsis}
/* render() adds .tight when the label still would not fit: the icon goes,
   the word stays whole. Only a really long word ellipsizes after that. */
.tab.on.tight svg{display:none}
/* The card game sits in the same seat in both bars: last on the right, in the
   kids green rather than the muted tab grey. In the grown-up bar it is a way
   out of that app, so a hairline and a wider gap set it apart from the three
   screens and it never lights up. In the kids bar it is a real tab — it takes
   the pill when you are on it (.on.k wins on specificity) and drops both the
   rule and the gap. */
.tab.jump{color:var(--green);margin-left:11px;position:relative}
.tab.jump:not(.k)::before{content:"";position:absolute;left:-8px;top:13px;bottom:13px;width:1px;background:var(--line)}
.tab.jump.k{margin-left:0}
.tab svg{stroke:currentColor;flex:none}
.dots{display:flex;align-items:center;gap:10px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--line)}
.dot.done{background:var(--green)} .dot.today{background:var(--yellow)}
.num{font-family:var(--serif);font-weight:300;letter-spacing:-.03em;line-height:1}
.serif{font-family:var(--serif)}
.ital{font-family:var(--serif);font-style:italic}
.big{font-family:var(--serif);font-weight:300;font-size:168px;line-height:1;letter-spacing:-.04em}
.list{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.li{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;padding:14px 16px;border-radius:18px;background:var(--white);box-shadow:var(--sh-pill);text-align:left;width:100%}
/* Label-over-input rows on the account screens — a list row's padding makes
   them needlessly tall, so they get their own tighter box. */
.li.field{display:block;padding:7px 16px 8px}
.li.field .eyebrow{display:block;font-size:10.5px;line-height:1.25;letter-spacing:.1em}
/* 16px keeps iOS from zooming the page when the field takes focus, so the
   row is tightened around the text rather than by shrinking it. */
.li.field input{display:block;line-height:1.25;height:21px}
/* A row that is a link (Profile → Legal, guides) must look exactly like a
   row that is a button: the browser's blue + underline would leak through
   otherwise (AK, sept. 2026: «поплыл дизайн кнопок»). */
a.li{color:inherit;text-decoration:none}
.li b{display:block;font-weight:600;font-size:15px}
.li span{font-size:12.5px;color:var(--muted)}
.li .age{font-size:12px;font-weight:600;color:var(--muted);white-space:nowrap}
.li.locked{opacity:.55}
.group{margin-top:22px}
.chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.chip{height:38px;padding:0 14px;border-radius:999px;background:var(--white);box-shadow:var(--sh-pill);font-size:13px;font-weight:500;display:inline-flex;align-items:center;gap:8px}
.chip.on{background:var(--ink);color:var(--cream);box-shadow:inset 0 2px 0 rgba(255,255,255,.12),inset 0 -3px 0 rgba(0,0,0,.35)}
.chip .plus{font-size:11px;color:var(--pink);font-weight:600}
.chip.on .plus{color:var(--yellow)}
.toggle{width:52px;height:30px;border-radius:999px;background:var(--line);position:relative;box-shadow:inset 0 2px 4px rgba(58,42,30,.12);transition:background .2s;flex:none}
.toggle::after{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(58,42,30,.3);transition:transform .2s}
.toggle.on{background:var(--green)} .toggle.on::after{transform:translateX(22px)}
.stars{display:flex;gap:10px;justify-content:center;margin-top:22px}
.stars svg{stroke:#D8C9E6;fill:none} .stars svg.on{fill:var(--yellow);stroke:#D9A400}
.toast{position:absolute;left:50%;bottom:110px;transform:translateX(-50%);background:var(--ink);color:var(--cream);padding:10px 16px;border-radius:999px;font-size:13px;font-weight:500;box-shadow:0 10px 24px rgba(58,42,30,.3);animation:toast 1.8s ease both;pointer-events:none;white-space:nowrap}
@keyframes toast{0%{opacity:0;transform:translate(-50%,10px)}12%{opacity:1;transform:translate(-50%,0)}80%{opacity:1}100%{opacity:0}}
.burst{position:absolute;pointer-events:none;left:0;top:0}
.burst i{position:absolute;width:10px;height:10px;border-radius:50%;animation:fly .8s ease-out forwards}
@keyframes fly{to{transform:translate(var(--dx),var(--dy)) scale(.2);opacity:0}}
.pop{animation:pop .3s ease}
@keyframes pop{0%{transform:scale(1)}40%{transform:scale(1.18)}100%{transform:scale(1)}}
.ring{transform:rotate(-90deg)}
.overlay{position:absolute;inset:0;background:rgba(58,42,30,.55);display:flex;align-items:flex-end;padding:20px;animation:fade .2s ease}
@keyframes fade{from{opacity:0}to{opacity:1}}
.sheet{width:100%;background:var(--cream);border-radius:28px;padding:24px;box-shadow:var(--sh-card)}
.stgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-top:16px}
.st{aspect-ratio:1;border-radius:18px;overflow:hidden;background:#fff;box-shadow:var(--sh-pill);position:relative}
.st img{width:100%;height:100%;object-fit:cover;display:block}
.st.locked img{filter:grayscale(1) opacity(.35)}
.st .lk{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:22px}
.check{display:flex;gap:12px;align-items:flex-start;padding:12px 14px;border-radius:16px;background:var(--white);box-shadow:var(--sh-pill);font-size:14px}
.check i{width:22px;height:22px;border-radius:50%;background:var(--green);display:flex;align-items:center;justify-content:center;flex:none;font-style:normal;color:#fff;font-size:13px;font-weight:700}
.muted{color:var(--muted)}
.mt8{margin-top:8px}.mt14{margin-top:14px}.mt22{margin-top:22px}.mt34{margin-top:34px}
.center{text-align:center}

/* Player stage with a video. The clip fills the card and everything else
   floats over it: the controls keep their ids so the timer code is unchanged,
   only their position moves so the movement itself stays visible. */
/* Квадрат, а не широкая полоса: упражнение снимают вертикально, и в
   приземистой карточке от кадра оставалось узкое окно поперёк корпуса.
   Высота задана в инлайне (сцена собирается строкой), поэтому здесь
   приходится перебивать её !important — как и всему остальному ниже.
   Пропорция держится сама, без подбора чисел: экран плеера не
   прокручивается, поэтому карточка забирает ровно ту высоту, что
   осталась (flex:1 1 auto), а ширину из неё выводит aspect-ratio —
   align-self:center нужен, чтобы ширина не растягивалась на всю
   колонку. max-height повторяет ширину колонки (480 минус поля
   экрана) — без него на высоком окне флекс отдавал бы высоты больше,
   чем есть ширины, и квадрат вытягивался бы вверх.
   Так он остаётся квадратом и на 844, и на 590 — и кнопка Set done
   никогда не уезжает за нижний край.
   min-height/min-width 350px (АК, окт. 2026) — пол ниже которого ролик
   не ужимается: на низких экранах (альбомная ориентация, маленький
   телефон, открытая клавиатура) flex:1 1 auto мог сжать карточку почти
   до нуля — замерено 350×106 и даже 440×0 на некоторых размерах.
   Экран плеера теперь рендерится с {scroll:true} (см. scrPlayer в
   app.js), так что пол не выталкивает низ карточки за рамки экрана —
   там, где не помещается, появляется прокрутка вместо обрезки. */
.stage.hasvid{flex:1 1 auto;min-height:350px;min-width:350px;height:auto!important;aspect-ratio:1/1;
  align-self:center;width:auto;max-width:100%;
  max-height:calc(min(100vw,480px) - 40px)}
.stagevid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:inherit;background:var(--purple)}
.stagescrim{position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:linear-gradient(180deg,rgba(58,42,30,.12) 0%,rgba(58,42,30,.30) 44%,rgba(58,42,30,.74) 100%)}
/* The stage's controls carry inline styles (the scene is built as a string),
   so these have to win outright — otherwise the button stays dead centre and
   covers the movement the clip is there to show. */
.stage.hasvid #playBtn{position:absolute!important;right:16px;bottom:20px;width:62px!important;height:62px!important;margin:0}
/* The set/rest pill sits on whatever the footage happens to show — give it
   its own solid backing so it stays readable over a bright frame (a light
   wall, the white rings: AK's TestFlight photo, 0.22.0). */
.stage.hasvid .bar{background:rgba(58,42,30,.35)}
.stage.hasvid .stagepill{background:rgba(58,42,30,.82)!important;color:var(--cream);box-shadow:0 2px 8px rgba(58,42,30,.35)}
.stage.hasvid .stagepill span{opacity:1!important}
.stage.hasvid .stagepill span:nth-child(3){opacity:.85!important}
/* Title + hint stack in one box, so a wrapped hint pushes the title up
   instead of overlapping it. Kept clear of the play button on the right. */
.stage.hasvid .stagetxt{position:absolute!important;left:20px;right:96px;bottom:22px;align-items:flex-start!important;gap:4px!important;text-align:left}
.stage.hasvid #stageT{position:static!important;font-size:18px!important;line-height:1.2;max-width:none;text-align:left;text-shadow:0 1px 3px rgba(58,42,30,.5)}
.stage.hasvid #stageS{position:static!important;margin:0!important;font-size:12px;line-height:1.3;max-width:none;text-align:left;text-shadow:0 1px 3px rgba(58,42,30,.5)}
/* Конструктор недели: карточка-приглашение на Today, чипы тем в
   конструкторе (те же плитки, чуть ниже), строки дней в предпросмотре.
   Плитка собственного плана — светлая, среди цветных читается как «своя». */
.planCard{width:350px;margin-top:26px;padding:16px 18px;border-radius:22px;background:var(--white);box-shadow:var(--sh-pill);display:flex;align-items:center;justify-content:space-between;gap:12px;text-align:left}
.planCard.col{flex-direction:column;align-items:flex-start}
.progTile.tPlan{--tileBg:#FDF6E7;--tileFg:var(--ink);--tileBadge:var(--yellow);--tileBadgeFg:var(--ink);
  --tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(233,161,59,.35), rgba(233,161,59,0) 60%);
  box-shadow:inset 0 0 0 1.5px rgba(58,42,30,.10),0 6px 14px rgba(58,42,30,.08)}
.themeChip{min-height:110px}
.themeChip.sel{box-shadow:0 0 0 2.5px var(--ink),0 10px 24px rgba(58,42,30,.22)}
.pill.seg-on{background:var(--ink);color:var(--cream)}
.pday{padding:12px 14px;border-radius:18px;background:var(--white);box-shadow:var(--sh-pill)}
.pday b{font-size:14px}
.pday-line{margin-top:7px;font-size:12.5px;line-height:1.8;color:var(--muted)}
.pday-it{color:var(--ink);font-weight:600}
.pday-it.rel{color:var(--muted);font-weight:500}
.pday-it i{font-style:normal;color:var(--pink);font-weight:700;font-size:11px}
.pday-arr{opacity:.4;margin:0 5px}
/* Programme tiles on Today. The Me card's language — purple ground, ring
   motif, serif name — at a size that lets ten of them sit under the day card
   without burying the Begin button above. */
/* Палитра A «Тёплое дерево» (вживую одобрена АК, сент. 2026): восемь
   оттенков из логотипа Kittenfield — какао, терракота, мёд, песок, эспрессо,
   шалфей, глина, светлая зелень. --tileFg — текст и кольца, --tileBadge —
   метка «вы на этой»: на тёмной плитке она кремовая, на светлой — чернильная,
   так что читается на любой из восьми. */
.progTile{position:relative;min-height:136px;padding:14px;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-end;gap:3px;text-align:left;overflow:hidden;border-radius:22px;
  --tileFg:var(--cream); --tileBadge:var(--cream); --tileBadgeFg:var(--ink);
  color:var(--tileFg);background:var(--tileGlow),var(--tileBg);
  --tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(255,255,255,.22), rgba(255,255,255,0) 62%);
  --tileBg:var(--purple)}
/* Текущая — не обводка, а подъём: тень глубже, метка сверху. */
.progTile.on{box-shadow:0 8px 18px rgba(58,42,30,.16),0 20px 40px rgba(58,42,30,.30)}
.progTile.tA{--tileBg:#5C3A28;--tileGlow:radial-gradient(380px 260px at 88% -6%, rgba(233,161,59,.45), rgba(233,161,59,0) 62%)}
.progTile.tB{--tileBg:#B85C38;--tileGlow:radial-gradient(360px 250px at 86% -8%, rgba(251,243,228,.35), rgba(251,243,228,0) 60%)}
.progTile.tC{--tileBg:#E9C46A;--tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(255,255,255,.42), rgba(255,255,255,0) 62%)}
.progTile.tD{--tileBg:#F1D9A8;--tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(255,255,255,.46), rgba(255,255,255,0) 60%)}
.progTile.tE{--tileBg:#3E2B22;--tileGlow:radial-gradient(380px 260px at 88% -6%, rgba(193,99,63,.5), rgba(193,99,63,0) 62%)}
.progTile.tF{--tileBg:#6E7F5E;--tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(251,243,228,.3), rgba(251,243,228,0) 60%)}
.progTile.tG{--tileBg:#E8A87C;--tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(233,161,59,.4), rgba(233,161,59,0) 62%)}
.progTile.tH{--tileBg:#CBD5B1;--tileGlow:radial-gradient(360px 250px at 88% -6%, rgba(255,255,255,.44), rgba(255,255,255,0) 62%)}
.progTile.tC,.progTile.tD,.progTile.tG,.progTile.tH{--tileFg:var(--ink);--tileBadge:var(--ink);--tileBadgeFg:var(--cream)}
.progTile .progName{position:relative;font-size:19px;line-height:1.12}
.progTile .progMeta{position:relative;font-size:12px;opacity:.82}
.progTile .progMin{position:relative;font-size:11.5px;font-weight:600;opacity:.6}
.progTile .progNow{position:absolute;top:11px;left:13px;padding:3px 8px;border-radius:999px;background:var(--tileBadge);color:var(--tileBadgeFg);font-size:9.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase}
.bar{position:absolute;left:0;right:0;bottom:0;height:5px;background:rgba(251,243,228,.18)}
.bar i{display:block;height:100%;background:var(--yellow);width:0;transition:width .3s linear}

/* ---- first-launch language screen (purple gradient, iOS-setup mood) ---- */
/* Also used by the Login/Register screen (scrAuth) — same glow so account
   entry reads as one continuous moment, not a jump to a plain page. */
.langWrap{position:relative;flex:1;display:flex;flex-direction:column;overflow:hidden;color:var(--cream);
  background:
    radial-gradient(520px 460px at 50% 122%, rgba(0,214,127,.26), rgba(0,214,127,0) 60%),
    var(--purple);}
.langWrap .rings{opacity:.9}
/* Two of the three glows drift slowly (the pink one top-right, the yellow
   one left) so the background feels alive without being distracting — a
   ~20s breathing cycle, not a spin. Positioned to sit exactly where the
   old static radial-gradient()s at 84%/-6% and -12%/20% used to. */
.langWrap::before,.langWrap::after{content:"";position:absolute;pointer-events:none;border-radius:50%}
.langWrap::before{width:480px;height:380px;left:calc(84% - 240px);top:calc(-6% - 190px);
  background:radial-gradient(480px 380px at center, rgba(193,99,63,.55), rgba(193,99,63,0) 60%);
  animation:glowDrift1 19s ease-in-out infinite}
.langWrap::after{width:420px;height:340px;left:calc(-12% - 210px);top:calc(20% - 170px);
  background:radial-gradient(420px 340px at center, rgba(233,161,59,.36), rgba(233,161,59,0) 60%);
  animation:glowDrift2 23s ease-in-out infinite}
@keyframes glowDrift1{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(-34px,30px) scale(1.06)}}
@keyframes glowDrift2{0%,100%{transform:translate(0,0) scale(1)}50%{transform:translate(28px,-24px) scale(1.05)}}
@media (prefers-reduced-motion:reduce){.langWrap::before,.langWrap::after{animation:none}}
.langTop{position:relative;padding:calc(20px + env(safe-area-inset-top)) 22px 0}
.langMark{font-size:13px;font-weight:700;letter-spacing:.3em;text-transform:uppercase;opacity:.72}
.langCenter{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;text-align:center;padding:0 30px}
.langWord{font-size:60px;letter-spacing:.01em;opacity:.94}
.langTag{font-size:14px;font-weight:500;letter-spacing:.02em;opacity:.62}
.langBody{position:relative;padding:0 22px calc(34px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:16px}
.langTitle{font-size:24px;line-height:1.2;letter-spacing:-.005em}
/* Language drum. Fixed height whatever the language count: rows scroll under
   a stationary selection band, snapping to it. --wheelH/--rowH drive both the
   band and the end pads, so the geometry stays consistent in one place. */
/* The row scheme is Sprouzy's (AK's other app): a two-letter code column on
   the left, the endonym left-aligned beside it, and the selected row sitting
   on one solid tinted band instead of between two hairlines in a boxed
   panel. Only the layout was borrowed — the colours stay WBRS's, cream on the
   purple gate. The code column is what lets someone find their language
   without being able to read the current one: ISO codes look the same in
   every script. The band is now what anchors the drum's centre, so the old
   faint panel behind the rows is gone. */
.wheel{--wheelH:232px;--rowH:44px;position:relative;height:var(--wheelH);margin-top:2px}
.wheelBand{position:absolute;left:0;right:0;top:50%;height:var(--rowH);transform:translateY(-50%);pointer-events:none;
  border-radius:14px;background:rgba(251,243,228,.13);box-shadow:inset 0 1px 0 rgba(251,243,228,.12)}
.wheelScroll{position:relative;height:100%;overflow-y:auto;overscroll-behavior:contain;scroll-snap-type:y mandatory;
  scrollbar-width:none;-ms-overflow-style:none;
  /* Five rows fit, and the gradient has to leave the outer two legible rather
     than erase them: it fades to half alpha at the ends instead of zero, and
     the fully-opaque band (22%-78%) covers exactly the middle three rows. */
  -webkit-mask-image:linear-gradient(to bottom,rgba(0,0,0,.5) 0,#000 22%,#000 78%,rgba(0,0,0,.5) 100%);
  mask-image:linear-gradient(to bottom,rgba(0,0,0,.5) 0,#000 22%,#000 78%,rgba(0,0,0,.5) 100%)}
.wheelScroll::-webkit-scrollbar{display:none}
.wheelPad{height:calc((var(--wheelH) - var(--rowH)) / 2)}
.wheelRow{display:flex;align-items:center;justify-content:flex-start;gap:14px;width:100%;height:var(--rowH);padding:0 18px;
  scroll-snap-align:center;text-align:left;line-height:1;color:var(--cream);will-change:opacity}
.wheelRow .code{flex:none;min-width:34px;text-align:center;text-indent:.14em;font-family:var(--sans);font-size:12px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;opacity:.5}
.wheelRow .name{font-family:var(--serif);font-size:22px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:font-size .12s ease}
.wheelRow.on .code{opacity:.9}
/* «Не жирным» (АК, сент. 2026): выбранный язык выделяет полоса барабана и
   лёгкое увеличение — вес шрифта не меняется. */
.wheelRow.on .name{font-weight:400;font-size:25px}
/* The drum and its two arrows. They sit above and below the scroller rather
   than inside it: the rows keep moving under the band, and an arrow floating
   over them would slide across the neighbouring language's code. Both are
   centred on the code column (18px of row padding + half of the 34px code
   cell), so "up" and "down" clearly belong to the code they bracket. */
.wheelBox{position:relative}
/* Bare chevrons, no button behind them: the same width as the code cell
   (18px of row padding, then the 34px cell) so each sits exactly over the
   two letters, and the same weight as the code itself. */
.wheelArrow{display:flex;align-items:center;justify-content:center;width:34px;height:18px;margin-left:18px;
  background:none;border:0;padding:0;color:var(--cream);opacity:.55;
  transition:opacity .15s ease}
.wheelArrow:hover{opacity:.9}
.wheelArrow:active{opacity:1}
.wheelArrow.off{opacity:.2;pointer-events:none}
.wheelArrow svg{width:15px;height:15px}
.wheelBox .wheel{margin-top:3px}
.wheelBox > .wheelArrow:last-child{margin-top:3px}

/* A short screen keeps five languages in view — the rows get shorter rather
   than fewer, because dropping to three is what made the picker feel like a
   list of three. The buttons underneath have to stay above the fold. */
@media (max-height:700px){ .wheel{--wheelH:195px;--rowH:39px} .wheelRow .name{font-size:20px} .wheelRow.on .name{font-size:23px} }
@media (max-height:600px){ .wheel{--wheelH:170px;--rowH:34px} .wheelRow .name{font-size:18px} .wheelRow.on .name{font-size:20px} .wheelArrow{height:16px} .wheelArrow svg{width:13px;height:13px} }

/* ---- activity cards (ported from the app) ---- */
/* Takes the space that's left rather than a fixed 508px: on a short phone
   (or a zoomed-in browser) the fixed height overflowed, and since this screen
   doesn't scroll the card ran under both the header and the tab bar. */
.deckArea{position:relative;flex:1;min-height:0;max-height:508px;display:flex;align-items:center;justify-content:center;margin:12px 0 10px}
.acard{position:absolute;height:100%;max-height:468px;max-width:100%;aspect-ratio:334 / 468;border-radius:24px;box-shadow:0 7px 14px rgba(58,42,30,.16),0 18px 40px rgba(58,42,30,.18);overflow:hidden}
.acard.back{display:flex;align-items:center;justify-content:center;flex-direction:column;border:6px solid var(--edge);background:var(--mode)}
.acard.back .paw{position:absolute;font-size:22px;opacity:.18}
.acard.back .disc{width:96px;height:96px;border-radius:50%;background:rgba(255,255,255,.92);display:flex;align-items:center;justify-content:center;font-size:48px;position:relative}
.acard.back .wm{margin-top:10px;font-weight:700;font-size:16px;letter-spacing:.14em;color:var(--wm);position:relative}
.acard.face{background:#FFFEFC;border:5px solid #E9D9C6;display:flex;flex-direction:column;color:#222;touch-action:none;user-select:none;cursor:grab}
.acard.face .ttl{min-height:52px;display:flex;align-items:center;justify-content:center;padding:14px 26px 4px;font-weight:600;font-size:17.5px;line-height:24px;text-align:center}
.acard.face .need{align-self:center;margin-top:7px;background:#FBF3E6;border:1.5px solid #EADCC6;border-radius:999px;padding:4px 12px;font-size:10.5px;font-weight:800;color:#8A5A2B}
.acard.face .art{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:4px 20px}
/* The card art ships as JPEG, so each illustration carries a solid white
   rectangle that showed as a square against the card's warm off-white.
   Multiply drops pure white to the backdrop and leaves the drawing. */
.acard.face .art img{max-width:100%;max-height:100%;object-fit:contain;pointer-events:none;mix-blend-mode:multiply}
.acard.face .plate{background:#FBF3E6;border:1.5px solid #EADCC6;margin:0 20px 14px;border-radius:18px;padding:12px 16px;min-height:58px;display:flex;align-items:center;justify-content:center;font-size:12.5px;line-height:18px;color:#3A2E22;text-align:center}
.acard.face .kind{align-self:center;margin-bottom:14px;display:flex;align-items:center;gap:7px;background:#fff;border:1.5px solid #EADCC6;border-radius:999px;padding:8px 18px;font-size:12.5px;font-weight:800;color:#8A5A2B}
.acard.top{animation:deal .45s cubic-bezier(.2,.9,.3,1.1) both;will-change:transform}
@keyframes deal{from{transform:translateY(70px) scale(.92);opacity:0}to{transform:none;opacity:1}}
.acard.top.drag{animation:none;transition:none}
.acard.top.spring{animation:none;transition:transform .35s cubic-bezier(.2,.9,.3,1.2)}
.acard.top.fling{animation:none;transition:transform .42s cubic-bezier(.5,0,.9,.4),opacity .42s}
.pdots{display:flex;justify-content:center;gap:8px;margin-top:10px}
.pdots span{height:10px;width:10px;border-radius:5px;background:var(--line);transition:width .2s}
.pdots span.done{background:var(--green)} .pdots span.cur{width:22px;background:var(--mode)}
.modeTile{display:flex;align-items:center;gap:16px;padding:20px;border-radius:22px;background:var(--mode);color:var(--ink);text-align:left;width:100%;box-shadow:inset 0 2px 0 rgba(255,255,255,.35),inset 0 -4px 0 rgba(58,42,30,.18),0 10px 24px rgba(58,42,30,.16)}
.modeTile .disc{width:60px;height:60px;border-radius:50%;background:rgba(255,255,255,.92);display:flex;align-items:center;justify-content:center;font-size:30px;flex:none}
.modeTile b{font-size:19px;font-weight:700;display:block}
.modeTile span{font-size:12.5px;opacity:.85}
.modeTile.calm{color:var(--cream)}

/* ---- Support chat (0.16.0) ------------------------------------------------
   Chat Hub's shared neutral skin (AK, Sept 2026): grey ground, white surfaces,
   one grey accent, system font — the same in every one of AK's apps, so the
   chat looks like one product wherever it appears. Scoped to .hub only; the
   rest of Kittenfield keeps its own palette. Values match chathub/theme.js. */
.hub{--h-bg:#F8F9FA;--h-surface:#FFFFFF;--h-ink:#2B333B;--h-muted:#6B7380;--h-caption:#8A94A0;--h-line:#E8ECEF;--h-soft:#F1F3F5;--h-accent:#5F6B78;--h-online:#2E8B57;--h-err:#D6304F;
  position:absolute;inset:0;display:flex;flex-direction:column;background:var(--h-bg);color:var(--h-ink);
  font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif}
.hubHead{display:flex;align-items:center;gap:12px;padding:calc(16px + env(safe-area-inset-top)) 16px 12px;background:var(--h-surface);border-bottom:1px solid var(--h-line);flex:none}
.hubHead .circ svg path{stroke:var(--h-ink)}
.hubHeadTxt{display:flex;flex-direction:column;min-width:0}
.hubHeadTxt b{font-size:17px;font-weight:700}
.hubHeadTxt span{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--h-muted)}
.hubDot{width:8px;height:8px;border-radius:50%;background:var(--h-online);display:inline-block}
.hubBody{flex:1;overflow-y:auto;padding:20px 16px calc(24px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:16px}
.hubCard{background:var(--h-surface);border-radius:16px;padding:18px;display:flex;flex-direction:column;gap:8px;box-shadow:0 1px 2px rgba(43,51,59,.05)}
.hubLabel{font-size:12.5px;font-weight:700;color:var(--h-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.hubFaq{border-top:1px solid var(--h-line)}
.hubFaq:first-of-type{border-top:0}
.hubFaq summary{list-style:none;cursor:pointer;min-height:48px;display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:15px;font-weight:600}
.hubFaq summary::-webkit-details-marker{display:none}
.hubFaq summary::after{content:'›';font-size:20px;color:var(--h-caption);transition:transform .15s}
.hubFaq[open] summary::after{transform:rotate(90deg)}
.hubFaq div{font-size:14px;line-height:1.45;color:var(--h-muted);padding-bottom:12px}
.hubCta{display:flex;align-items:center;gap:12px}
.hubCta b{display:block;font-size:17px}
.hubCta span{font-size:13px;color:var(--h-muted);font-weight:600}
.hubCtaIcon{width:40px;height:40px;border-radius:50%;background:var(--h-accent);color:#fff;display:flex;align-items:center;justify-content:center;font-size:18px;flex:none}
.hubBtn{height:52px;border-radius:16px;background:var(--h-accent);color:#fff;font-size:16px;font-weight:700;margin-top:6px;width:100%}
.hubNote{font-size:13.5px;line-height:1.4;color:var(--h-muted);margin-top:4px}
.hubList{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
.hubMsg{max-width:82%;display:flex;flex-direction:column;gap:4px;align-self:flex-start}
.hubMsg.me{align-self:flex-end;align-items:flex-end}
.hubWho{font-size:11px;font-weight:700;color:var(--h-muted);letter-spacing:.04em;text-transform:uppercase;padding-left:6px}
.hubBubble{padding:10px 14px;border-radius:18px;font-size:15px;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere}
.hubMsg.me .hubBubble{background:var(--h-accent);color:#fff;border-bottom-right-radius:6px}
.hubMsg.bot .hubBubble{background:var(--h-soft);border-bottom-left-radius:6px}
.hubMsg.person .hubBubble{background:var(--h-surface);border:1px solid var(--h-line);border-bottom-left-radius:6px}
.hubTime{font-size:11px;font-weight:600;color:var(--h-caption);padding:0 6px}
.hubEmpty{margin:auto;text-align:center;color:var(--h-muted);font-size:14px;max-width:260px;line-height:1.45}
.hubErr{color:var(--h-err);font-size:12.5px;padding:0 16px;min-height:0}
.hubErr:empty{display:none}
.hubComposer{display:flex;align-items:flex-end;gap:8px;padding:10px 12px calc(12px + env(safe-area-inset-bottom));background:var(--h-surface);border-top:1px solid var(--h-line);flex:none}
.hubComposer textarea{flex:1;min-height:44px;max-height:120px;resize:none;border:1px solid var(--h-line);border-radius:22px;padding:11px 16px;font:inherit;font-size:15px;color:var(--h-ink);background:var(--h-bg);outline:none}
.hubComposer textarea:focus{border-color:var(--h-accent)}
.hubSend{width:44px;height:44px;border-radius:50%;background:var(--h-accent);color:#fff;display:flex;align-items:center;justify-content:center;flex:none}
.hubSend:disabled{opacity:.4}
.hubLoading{display:flex;gap:6px;justify-content:center;padding:18px}
.hubLoading i{width:8px;height:8px;border-radius:50%;background:var(--h-caption);animation:hubDot 1s infinite ease-in-out}
.hubLoading i:nth-child(2){animation-delay:.15s}.hubLoading i:nth-child(3){animation-delay:.3s}
@keyframes hubDot{0%,80%,100%{opacity:.25}40%{opacity:1}}
