/* natalka/app/static/app.css — shell layout: loader (frame L) + safe-area page frame.
   Screen content lives in partials; this file only sets the frame every screen shares. */

* { box-sizing: border-box; }

/* Task 4.4 fix: a `hidden` element whose own class sets `display` (e.g. `.chip`,
   `friend-date-lock-invite`) otherwise stays visible — a class selector outranks the UA
   `[hidden]` rule's specificity. One global override, ahead of every other rule in this file. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--t-body);
  -webkit-font-smoothing: antialiased;
}

body {
  padding-top: var(--tg-content-safe-area-inset-top, 0px);
  padding-bottom: var(--tg-content-safe-area-inset-bottom, 0px);
  padding-left: var(--tg-content-safe-area-inset-left, 0px);
  padding-right: var(--tg-content-safe-area-inset-right, 0px);
  min-height: 100vh;
}

#screen {
  padding: 0 var(--pad-x);
  opacity: 0;
  transition: opacity var(--unblur-ms) var(--ease-out);
}

body.ready #screen { opacity: 1; }

/* Frame L — full-screen loader, visible until body.ready */
.loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--loader-bg);
  transition: opacity 200ms var(--ease-out), visibility 200ms var(--ease-out);
}

.loader-monogram {
  width: 96px;
  height: 96px;
  transform: translateY(-12vh); /* monogram centre sits at ~44% of screen height */
}

.loader-monogram svg { width: 100%; height: 100%; display: block; }

body.ready .loader {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Voice field (product) — DIRECTION.md "System beyond chat": ONE flat sunflower field per
   screen carrying Natalka's current line; no speaker row outside chat — the field itself
   carries a 24px monogram top-right (inset 14px), text right-padded to clear it. */
.voice-field {
  position: relative;
  margin-top: var(--gap-l);
  background: var(--voice);
  color: var(--voice-ink);
  border-radius: var(--r-card);
  padding: 16px 48px 16px 16px;
}

.voice-field .voice-line {
  margin: 0;
  font: var(--t-voice);
  font-variation-settings: var(--flar-voice);
}

.voice-field .monogram-badge {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 24px;
  height: 24px;
}

.voice-field .monogram-badge svg { width: 100%; height: 100%; display: block; }

/* Onboarding screens (Task 2.6, style C, DIRECTION frames A/B) */

.progress-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--gap-m);
}

.progress-label { font: var(--t-meta); color: var(--muted); }

.progress-track { display: flex; gap: var(--gap-xs); }

.progress-seg { width: 20px; height: 4px; border-radius: 2px; background: var(--progress-off); }
.progress-seg.on { background: var(--accent); }

.card {
  margin-top: var(--gap-m);
  background: var(--surface);
  border-radius: var(--r-card);
  padding: var(--gap-l);
}

.card-title { font: var(--t-title); color: var(--text); }

.rows { margin-top: var(--gap-m); display: flex; flex-direction: column; gap: 10px; }

.row-label { font: var(--t-body); font-weight: 600; color: var(--text); display: inline-block; }

.row-label.marked {
  background: linear-gradient(transparent 55%, var(--marker) 55%);
  background-size: 100% 100%;
  padding: 0 3px;
}

.row-text {
  margin-top: var(--gap-xs);
  font: var(--t-body);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-text.open { white-space: normal; }

.row-text.blurred {
  filter: blur(var(--blur));
  opacity: var(--blur-opacity);
  user-select: none;
  height: 1em;
  background: var(--line);
  border-radius: 4px;
}

.row-text.skeleton { color: var(--muted); }

.row.reveal .row-label.marked { background-size: 0 100%; animation: mark-sweep var(--marker-ms) var(--ease-out) var(--marker-delay) forwards; }
.row.reveal .row-text.open { filter: blur(var(--blur)); opacity: var(--blur-opacity); animation: unblur var(--unblur-ms) var(--ease-out) forwards; }

@keyframes mark-sweep { to { background-size: 100% 100%; } }
@keyframes unblur { to { filter: blur(0); opacity: 1; } }
@keyframes calc-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .row.reveal .row-label.marked { animation: none; background-size: 100% 100%; }
  .row.reveal .row-text.open { animation: none; filter: blur(0); opacity: 1; }
  .calc-progress-fill { transition: none; }
  .calc-item.active .calc-mark { animation: none; }
}

.speaker-row { display: flex; align-items: center; gap: var(--gap-s); margin-top: var(--gap-l); }
.speaker-row .avatar { width: var(--avatar); height: var(--avatar); flex: none; display: block; }
.speaker-row .avatar svg { width: 100%; height: 100%; display: block; }
.speaker-name { font: var(--t-meta); color: var(--muted); }

.voice-line.plain { margin-top: var(--gap-s); font: var(--t-voice); font-variation-settings: var(--flar-voice); color: var(--text); }

.reply {
  margin-top: var(--gap-m);
  margin-left: auto;
  width: fit-content;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-reply);
  padding: 0 20px;
  height: 48px;
  display: flex;
  align-items: center;
  font: 600 17px/22px var(--font);
  color: var(--text);
}

.voice-field.tail { border-radius: var(--r-voice); margin-top: var(--gap-m); }
.vf-meta { font: var(--t-meta); color: var(--voice-ink); opacity: 0.7; }
.vf-text { margin-top: var(--gap-xs); font: var(--t-voice); font-variation-settings: var(--flar-voice); color: var(--voice-ink); }
.vf-text.no-meta { margin-top: 0; }
.vf-link { display: inline-block; margin-top: 10px; font: 600 15px/20px var(--font); color: var(--voice-ink);
  text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 2px; }

.date-field { margin-top: var(--gap-l); }
.field-label { display: block; font: var(--t-label); color: var(--text); margin-bottom: var(--gap-xs); }
.field-hint { font: var(--t-body); color: var(--muted); margin-top: var(--gap-s); }
.field-error { font: var(--t-meta); color: var(--text); margin-top: var(--gap-xs); }

input[data-role], .name-input, .country-input {
  width: 100%;
  height: 48px;
  padding: 0 16px;
  border-radius: var(--r-chip);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font: var(--t-body);
}
.screen-name .name-input { margin-top: var(--gap-m); }   /* canon №137: step 1, the field alone under her line */

/* canon №137 follow-up (builder's call №111, 2026-09-25): visible "work" on the calculating
   screen — a thin bar filling toward completion (JS sets its width/duration, app.js token-only
   colours), and a checklist that accumulates one line at a time, the current one spinning,
   finished ones checked off and dimmed. */
.calc-progress { margin-top: var(--gap-m); height: 3px; border-radius: 2px; background: var(--progress-off); overflow: hidden; }
.calc-progress-fill { height: 100%; width: 0%; background: var(--accent); transition-property: width; transition-timing-function: linear; }

.calc-checklist { margin-top: var(--gap-m); list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--gap-s); }
.calc-item { display: flex; align-items: center; gap: var(--gap-s); }
.calc-item .calc-text { font: var(--t-voice); font-variation-settings: var(--flar-voice); color: var(--voice-ink); }
.calc-item.done .calc-text { color: var(--muted); }

.calc-mark { width: 16px; height: 16px; flex: none; border-radius: 50%; box-sizing: border-box; }
.calc-item.active .calc-mark { border: 2px solid var(--progress-off); border-top-color: var(--accent); animation: calc-spin 800ms linear infinite; }
.calc-item.done .calc-mark { background: var(--accent); position: relative; }
.calc-item.done .calc-mark::after {
  content: ""; position: absolute; left: 4px; top: 2px; width: 5px; height: 8px;
  border: solid var(--accent-ink); border-width: 0 2px 2px 0; transform: rotate(45deg);
}

/* the retry state (showChartError) reuses .calc-item for its two lines + the retry chip, stacked
   instead of the usual mark+text row. */
.calc-item.calc-error { display: block; }
.calc-item.calc-error .calc-text { display: block; }
.calc-item.calc-error .chips { margin-top: var(--gap-s); }

.chips { margin-top: var(--gap-m); display: flex; flex-wrap: wrap; gap: var(--gap-s); }

.chip {
  height: 40px;
  padding: 0 16px;
  border-radius: var(--r-chip);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font: var(--t-chip);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.chip .pencil { width: 14px; height: 14px; flex: none; display: block; }

/* Task 4.4: a native `disabled` button keeps this class's own colours otherwise — dim it the
   same way .guess-chips already dims a tap in flight, everywhere a `.chip` can be disabled
   (chart.slots's manual-friend submit until the form is valid). */
.chip:disabled { opacity: 0.5; pointer-events: none; }

/* canon №126 (D): ONE style for every in-page button — secondary = the outlined .chip above,
   primary = filled with the ink accent («Так, надсилай», «Надіслати запрошення», …). */
button.chip { appearance: none; -webkit-appearance: none; cursor: pointer; }
.chip.chip-primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.chip.selected, .chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: transparent;
}

.subfield, .place-field, .born-ukraine { margin-top: var(--gap-m); }

.relatives-button { appearance: none; -webkit-appearance: none; cursor: pointer; margin-top: var(--gap-s); }
.relatives-hint { margin-top: var(--gap-s); }

.place-results { list-style: none; margin: var(--gap-s) 0 0; padding: 0; }
.place-results li {
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  font: var(--t-body);
  color: var(--text);
}

/* Dark theme: --nm-tile and --voice are both #F5D547 there, so the badge's tile blends into the
   field it sits on and disappears. Reuse the MainButton pair (same yellow-on-dark duality
   already in tokens.css) instead of inventing new colours: dark tile, yellow letters. */
[data-theme="dark"] .voice-field .monogram-badge {
  --nm-tile: var(--main-btn-text);
  --nm-ink: var(--main-btn);
}

/* Chart screen (Task 3.4, DIRECTION frame C): the finished "Коротко про тебе" card sits on the
   sunflower field, r20 padding 20 — the ONLY yellow on this screen; everything below is quiet
   ink/grey (`.chart-section`, DIRECTION "System beyond chat" §heading/§sub tokens). */
.voice-card {
  margin-top: var(--gap-l);
  background: var(--voice);
  color: var(--voice-ink);
  border-radius: var(--r-card);
  padding: 20px;
}

.voice-card .cof-title { font: var(--t-display); letter-spacing: var(--ls-display); color: var(--voice-ink); }
.voice-card .cof-body { margin-top: var(--gap-m); }
.voice-card .cof-body p { margin: var(--gap-m) 0 0; font: var(--t-body); color: var(--voice-ink); }
.voice-card .cof-body p:first-child { margin-top: 0; }
.voice-card .cof-body strong { font-weight: 600; }
/* №125: a label is its own paragraph, never smaller than the text under it */
.voice-card .cof-body .run-label { font: var(--t-body); font-weight: 600; margin-bottom: calc(var(--gap-xs) - var(--gap-m)); }

.chart-section { margin-top: var(--gap-xl); }
.chart-section .section-heading { font: var(--t-display); letter-spacing: var(--ls-display); color: var(--text); }
.chart-section .section-sub { margin-top: var(--gap-xs); font: var(--t-sub); color: var(--muted); }
.chart-section .section-lead { margin: var(--gap-m) 0 0; font: var(--t-voice-strong); color: var(--text); }
/* canon №126 — one skeleton per sign section: traits as short lines, the scene in Natalka's
   field, «Звідси сила/ціна» as labelled blocks, «Перевір на собі» last as the reader's task. */
.chart-section .traits { list-style: none; margin: var(--gap-l) 0 0; padding: 0; }
.chart-section .traits li { position: relative; padding: 0 0 0 16px; margin-top: var(--gap-s); font: var(--t-body); color: var(--text); }
.chart-section .traits li:first-child { margin-top: 0; }
.chart-section .traits li::before { content: ""; position: absolute; left: 2px; top: 9px; width: 5px; height: 5px; border-radius: 50%; background: var(--muted); }
.chart-section .scene { margin-top: var(--gap-l); padding-right: 16px; }
.chart-section .scene .voice-line p { margin: var(--gap-s) 0 0; }
.chart-section .scene .voice-line p:first-child { margin-top: 0; }
.sign-block { margin-top: var(--gap-m); background: var(--surface); border-radius: var(--r-card); padding: 14px var(--gap-l); font: var(--t-body); color: var(--text); }
.sign-block p { margin: 0; }
.sign-block .run-label { font: var(--t-label); margin-bottom: var(--gap-xs); }
.sign-block.task { background: transparent; border: 1.5px dashed var(--line); }
.chart-section .sign-extra { margin-top: var(--gap-m); color: var(--muted); }
.chart-section .sign-extra blockquote { margin: var(--gap-m) 0 0; padding: 0 0 0 var(--gap-m); border-left: 2px solid var(--line); }
.chart-section .sign-extra blockquote:first-child { margin-top: 0; }
.chart-section .sign-extra blockquote p { margin: 0; }
/* The farewell ends the chart (canon №127): Natalka's own line, nothing sold in it. */
.farewell { margin-top: var(--gap-xl); font: var(--t-voice); font-variation-settings: var(--flar-voice); color: var(--text); }
.farewell p { margin: 0; }
.chart-section .vf-link { color: var(--accent); }         /* not on a voice field here — override */
.chart-section .section-body { margin-top: var(--gap-m); font: var(--t-body); color: var(--text); }
.chart-section .section-body p { margin: var(--gap-m) 0 0; }
.chart-section .section-body p:first-child { margin-top: 0; }
.chart-section .section-body strong { font-weight: 700; }
.chart-section .section-body .run-label { font: var(--t-body); font-weight: 600; margin-bottom: calc(var(--gap-xs) - var(--gap-m)); }
.chart-section .section-body ol, .chart-section .section-body ul { margin: var(--gap-m) 0 0; padding-left: 20px; }
.chart-section .section-body li { margin-top: var(--gap-xs); }

/* Task 5.3: offer.transition, right after the upsell section — Natalka's own voice (line 1),
   then the price-free promise in the bolder --t-voice-strong weight (line 2, №98). No card, no
   yellow field: same quiet ink-on-bg surface as the sections around it. */
.offer-field { margin-top: var(--gap-xl); }
.offer-field .offer-line { margin-top: var(--gap-m); font: var(--t-voice); color: var(--text); }
.offer-field .offer-line:first-child { margin-top: 0; }
.offer-field .offer-line-strong { font: var(--t-voice-strong); }
.offer-field > .chart-section:first-child { margin-top: 0; }
.offer-more { margin-top: var(--gap-m); }
.offer-more > summary { list-style: none; color: var(--accent); }
.offer-more > summary::-webkit-details-marker { display: none; }
.offer-more .chart-section { margin-top: var(--gap-m); }

/* Chart-to-chat prompt (Task 3.5, save.ask): a quiet service card, not a line from Natalka
   herself — same surface as the onboarding `.card`, not the yellow `.voice-card`, and sits after
   the last chart section with the same rhythm as one (`--gap-xl`). */
.save-ask {
  margin-top: var(--gap-xl);
  background: var(--surface);
  border-radius: var(--r-card);
  padding: var(--gap-l);
}
.save-ask-text { margin: 0; font: var(--t-body); color: var(--text); }
.save-ask .chips { margin-top: var(--gap-m); }
.tail-steps > .save-ask:first-child { margin-top: var(--gap-xl); }

/* Task 4.3: the g_ guess screen — same .chip as elsewhere, just tappable and dimmed while the
   answer is in flight. */
.guess-chips .chip { cursor: pointer; }
.guess-chips .chip.disabled { opacity: 0.5; pointer-events: none; }

/* Task 4.4: chart.slots — the friend-places block reuses .save-ask's surface; only the parts
   .save-ask does not already cover (the accepted-friends list, the manual form's checkbox row)
   get their own rule here. */
.slots-count { color: var(--muted); }
.slots-list { list-style: none; margin: var(--gap-m) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--gap-xs); }
.link-button {
  appearance: none; -webkit-appearance: none; border: none; background: none; cursor: pointer;
  padding: 0; font: var(--t-body); color: var(--accent); text-decoration: underline;
  text-underline-offset: 3px; text-align: left;
}
.chip-check { display: flex; align-items: center; gap: 8px; margin-top: var(--gap-m); font: var(--t-body); color: var(--text); }
.chip-check input { width: 18px; height: 18px; flex: none; }

/* Task 9: full-chart contents/chapter screens — style C, no new colours or fonts. The chapter
   title/body and the "Що з цим робити" block reuse .chart-section/.section-heading/.section-body
   verbatim (same look as the short chart's own body sections); the "Далі"/"До змісту" pair reuses
   .chips (same as save-ask's yes/no row); the mid-chapter Natalka quote reuses .voice-field, just
   without its reserved monogram-badge gutter (`.plain`) since no badge sits in it here. Only the
   contents list itself (row/locked/unread) has no existing analogue, so it gets its own rules. */
.voice-field.plain { padding-right: var(--pad-x); }
.voice-field .voice-line p { margin: 0; font: inherit; }

.full-contents .contents { list-style: none; margin: var(--gap-m) 0 0; padding: 0; }
.full-contents .contents-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--gap-s);
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.full-contents .contents-row:last-child { border-bottom: none; }
.full-contents .contents-row .title { font: var(--t-body); color: var(--text); }
.full-contents .contents-row.locked .title { color: var(--muted); }
.full-contents .contents-row .note { font: var(--t-meta); color: var(--muted); text-align: right; }
.full-contents .contents-row .mark {
  font: var(--t-meta); color: var(--text); padding: 2px 6px; border-radius: 4px;
  background: linear-gradient(transparent 55%, var(--marker) 55%);
}

/* "why?" explanation opened under its link (app.js delegated handler) */
.vf-link { cursor: pointer; }
.vf-why { margin: 8px 0 0; font: 400 15px/22px var(--font); color: inherit; }
[data-role="chart-end"] { height: 1px; }

/* canon №133: the chart-ending deck — one full-height card at a time (static/ending.js) */
.ending { margin-top: var(--gap-xl); }
.ending-card { min-height: calc(var(--tg-viewport-stable-height, 100vh) - 2 * var(--gap-xl)); display: flex; flex-direction: column; justify-content: center; gap: var(--gap-m); }
.ending-card[hidden], .ending-footer[hidden], .build-line[hidden], [data-role="offer-lines"][hidden], .fm-step[hidden] { display: none; }
.deck-dots { display: flex; gap: 6px; justify-content: center; }
.deck-dot { width: 18px; height: 4px; border-radius: 2px; background: var(--progress-off); }
.deck-dot.on { background: var(--accent); }
.build-lines { list-style: none; margin: var(--gap-m) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--gap-s); }
.build-line::before { content: "🔒 "; }
.build-line.tick::before { content: "✓ "; }
.fade-lines { margin-top: var(--gap-m); display: flex; flex-direction: column; gap: 8px; }
.fade-bar { display: block; height: 13px; border-radius: 4px; background: var(--line); }
.fade-bar:nth-child(1) { width: 94%; }
.fade-bar:nth-child(2) { width: 86%; }
.fade-bar:nth-child(3) { width: 60%; opacity: .6; }
.locked-box { margin-top: var(--gap-m); padding: 12px 14px; border-radius: var(--r-card); border: 1.5px dashed var(--line); }
.locked-box .lk-title { margin: 0; font: var(--t-chip); color: var(--chart-ink); }
.locked-box .lk-meta { margin: 4px 0 0; font: var(--t-meta); color: var(--muted); }
.ending-footer { padding: var(--gap-l) 0; text-align: center; }
.quiet-link { text-decoration: underline; text-underline-offset: 3px; }
.cut-mark { display: block; margin: var(--gap-s) 0; font: var(--t-meta); color: var(--muted); }
.mark-started { color: var(--accent); }
.just-revealed { animation: just-revealed 1.2s var(--ease-out) 1; }
@keyframes just-revealed { from { background: var(--marker); } to { background: transparent; } }
