/* Mode A ("translation only", plan section 9.2) — the default display
   mode. Written mobile-first from a 390px base width (verified down to
   360px too) and expanded upward with min-width media queries — never
   max-width. The plan calls this out explicitly: writing it desktop-first
   reliably produces a layout that looks fine on a monitor and breaks on a
   phone, and the phone is the primary platform here.

   Step 12 added: 100dvh, overscroll-behavior, the auto-hiding #controls
   panel, safe-area-inset padding, 44px tap targets, user-select:none on
   buttons. visibilitychange/wake-lock/sampleRate handling and the
   enumerateDevices() mic picker live in app.js, not here.

   Still not done, on purpose (later steps):
   - Pause / Fullscreen / "copy transcript" buttons (plan 9.5 — not in this
     step's explicit scope)
   - Real-device verification of dvh/safe-area/etc. — this file only gets
     you correctness on paper; iOS Safari specifics need a real phone
     (step 15)

   Pico CSS (public/vendor/pico.min.css, loaded in index.html before this
   file) supplies base styling for #controls' form elements (buttons,
   inputs, select, the login <article> card) — forced into its dark palette
   via <html data-theme="dark"> so it matches this file's black/white
   subtitle aesthetic instead of following the OS color-scheme. It is
   deliberately NOT relied on anywhere inside #segments: the rules below for
   .segment/.segment-original/.segment-translation/#rotate-hint set color,
   font-size and margin explicitly so the carefully-tuned subtitle
   typography can never silently drift if Pico's own defaults change.
*/

:root {
  /* Mobile-first default (plan 9.2: 32-48px on phone). Only ever
     overridden two ways: this media query's desktop value below, or a
     user's explicit +/- choice persisted to localStorage (see app.js
     initFontSize/adjustFontSize) — whichever set --font-size last wins,
     which is the behavior we want: no saved preference means "follow the
     responsive default for this viewport", a saved preference means
     "the user chose this size on purpose, keep it". */
  --font-size: 40px;

  /* Post-step-11 tuning: fit more visible text on screen without shrinking
     --font-size (the plan is explicit that the large readable subtitle
     text is the whole point of a phone-first service — see CLAUDE.md
     "НЕ опускай мінімальний розмір шрифту"). The knobs used to buy back
     that space are vertical: a tighter gap between segments and a tighter
     line-height, both mobile-first values, both named here rather than
     inlined into the rules below so they stay one-line tweaks. Desktop
     gets a slightly more generous gap back in the min-width override,
     since screen real estate isn't the constraint there. */
  --segment-gap: 8px;
  --segment-line-height: 1.2;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #000;
  color: #fff;
  /* Belt-and-suspenders with #segments' own overscroll-behavior: pull-to-
     refresh is a browser-level gesture on the topmost scrollable ancestor,
     which may be the page itself (html/body) rather than #segments,
     depending on which element actually overflows. Setting it on both
     covers either case. */
  overscroll-behavior: none;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* Persistent, always-visible status indicators (plan 9.5: "Індикатор
   стану з'єднання в кутку" — a corner indicator). position:fixed, own
   stacking context above #controls, so it's never affected by the
   auto-hide timer and never depends on #controls' height (see the
   #controls comment below for why that matters). pointer-events:none so a
   tap landing on this small pill still bubbles to the document-level
   showControls() listener in app.js instead of being swallowed here. */
#status-bar {
  position: fixed;
  top: calc(8px + env(safe-area-inset-top));
  right: calc(8px + env(safe-area-inset-right));
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  background: rgba(15, 15, 18, 0.72);
  border-radius: 8px;
  pointer-events: none;
}

#status,
#connection-status,
#wake-lock-warning {
  margin: 0;
  color: #fff;
  font-size: 13px;
}

#wake-lock-warning {
  color: #ffcc66; /* mild warning tint, not alarming red */
}

/* --- Controls panel (plan 9.4, 9.5) ---------------------------------------
   Groups every interactive control (login, start/stop, font size, mode
   switch, column order, mic picker) into one auto-hiding panel: "під час
   розмови екран має бути зайнятий текстом, а не кнопками". Hidden with
   opacity+pointer-events (never display:none, which would make it jump
   *while animating* — a mid-fade layout reflow instead of a clean fade)
   after CONTROLS_HIDE_DELAY_MS of inactivity (app.js), and restored by a
   tap/click anywhere on the page.

   position:fixed (not normal document flow) is load-bearing, not
   decorative: this panel can be tall (login card + three control rows),
   and while it used to sit in normal flow, opacity-only hiding meant it
   kept reserving that full height even while invisible — on a short/wide
   viewport that invisible block could cover most of the screen and push
   #segments' actual content below the fold until a tap re-revealed it.
   Fixed positioning removes it from the flow entirely, so #segments always
   starts at the very top of the viewport regardless of #controls' own
   height or visibility. max-height + overflow-y is a safety net for the
   case where the panel's content is still taller than a very short
   landscape viewport even at max-height. */
#controls {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 100dvh;
  overflow-y: auto;
  padding: 12px 16px;
  /* iPhones with a notch/home-indicator: keep controls clear of them. */
  padding-top: calc(12px + env(safe-area-inset-top));
  padding-left: calc(16px + env(safe-area-inset-left));
  padding-right: calc(16px + env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: 8px;
  opacity: 1;
  transition: opacity 0.3s ease, background-color 0.3s ease;
  /* A translucent dark backdrop makes this read as a floating toolbar over
     the subtitles rather than a plain continuation of the black page. */
  background: rgba(15, 15, 18, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

#controls.controls--hidden {
  opacity: 0;
  pointer-events: none;
}

#controls button {
  min-width: 44px;
  min-height: 44px;
  /* A long press on a button must never bring up the text-selection
     menu (plan 9.4). Inputs are deliberately excluded — the password
     field still needs normal text selection/paste. */
  user-select: none;
  -webkit-user-select: none;
}

#controls select,
#controls input {
  min-height: 44px;
}

#capture-controls,
#font-size-controls,
#mode-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Login card: Pico's <article> already gives it a background/padding/
   radius (dark theme via html[data-theme="dark"]); this just keeps it a
   readable width and centered once the viewport is wider than a phone. */
#login-section {
  width: 100%;
  max-width: 420px;
  margin: 0 auto;
}

#login-section > header {
  font-size: 1.1rem;
  font-weight: 600;
}

/* --- Mode A: segment list ------------------------------------------------ */

#segments {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* Was a flat 12px; see the --segment-gap comment in :root for why this
     is now a mobile-first custom property instead of a literal. */
  gap: var(--segment-gap);
  padding: 16px;
  /* #controls is now position:fixed and overlays the top of the screen
     (see its comment), so this box's own top edge is the actual top of the
     viewport, notch and all — pad it with the safe-area inset too, not
     just left/right, so a phone with many stacked segments never puts text
     under a camera cutout (plan 9.4/12.21). */
  padding-top: calc(16px + env(safe-area-inset-top));
  padding-left: calc(16px + env(safe-area-inset-left));
  padding-right: calc(16px + env(safe-area-inset-right));
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
  /* justify-content:flex-end + min-height (not height) is what makes this
     work as BOTH "pinned to the bottom when short" AND "a normal scrolling
     history when long": while there are only a few segments (shorter than
     one screen), the flex box is exactly 100dvh tall and flex-end packs
     that handful of lines against the bottom edge, avoiding an awkward
     empty gap above them. Once segments accumulate past one screenful
     (plan revision: full history now stays in the DOM, see app.js's
     MAX_STORED_SEGMENTS comment), the box's height grows past 100dvh to
     fit its content — the natural chronological DOM order takes over
     top-to-bottom, and the PAGE itself becomes the scroll container (no
     `overflow` is set here on purpose — see app.js's isPageNearBottom/
     scrollToBottomIfSticking, which scroll window, not this element).
     #controls is position:fixed (see its own comment above) and no longer
     part of normal document flow, so none of this needs to account for a
     taller/shorter controls panel. */
  /* dvh (dynamic viewport height) accounts for Safari's address bar
     showing/hiding — 100vh is measured without the address bar and clips
     the bottom of the screen once it's visible (plan 9.4). */
  min-height: 100dvh;
  /* A stray swipe over the subtitles must never trigger pull-to-refresh
     mid-conversation — plan 9.4 calls this out as the worst possible bug
     here. See also the html/body rule below (this alone isn't always
     enough — see comment there). */
  overscroll-behavior: none;
}

.segment {
  margin: 0;
  color: #fff;
  font-size: var(--font-size);
  line-height: var(--segment-line-height);
  opacity: 1;
  /* Only the translation is shown once it exists (plan 9.2: "Тільки
     переклад"). Before a translation has arrived, the original is shown
     instead purely so the user sees *something* update instantly (plan
     8.1 p.1's "immediate reaction" principle) — see the displayText logic
     in app.js's renderSegments(). Once the translation lands it replaces
     the original text right here, in place. */
  /* A single very long unbroken token (a URL, a long German compound word
     ASR didn't split) must wrap rather than force the whole segment list
     into horizontal scroll — that would fight the bottom-anchored,
     vertical-only layout this container assumes everywhere else. */
  overflow-wrap: anywhere;
}

/* Plan 9.2 originally used this class to fade every visible segment except
   the newest ("верхні пригашені до 50%"), back when old segments were
   dropped from the DOM once they scrolled out of a fixed 2-3/3-5 segment
   window. Revised: the plan owner asked for old text to stay on screen,
   scrollable, rereadable — like Google Translate's mobile conversation
   view — instead of disappearing. Permanently dimming history you can
   deliberately scroll back to read would fight that directly, so this is
   now applied only to the single segment still being transcribed
   (isFinal === false necessarily means it's the last one, see the
   segment data model in app.js) as an "in progress, not committed yet"
   cue. Once a segment finalizes, app.js removes this class and its text
   never changes or fades again. */
.segment--dim {
  opacity: 0.5;
}

/* Minimal font-size +/- controls (plan 9.2). Colors/border now come from
   Pico's "outline secondary" button classes on the markup (index.html) —
   this only sets the sizing Pico doesn't know about (big glyph, square
   44px tap target) so localStorage persistence has a control that's easy
   to hit on a phone. */
#font-size-controls button {
  font-size: 20px;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
}

/* --- Mode B: paired original + translation (plan 9.3) --------------------
   Mobile-first default here is PORTRAIT: a single column, pairs stacked
   vertically (original, then its translation directly below). This is the
   plan's explicit "1fr" default — not a media query override of anything,
   it's simply what a 1-column grid means for a flat list of alternating
   original/translation elements: each gets its own row, in order.

   Landscape gets the real 2-column paired layout, switched ONLY by the
   `orientation` media query below — never by JS. Because each segment
   contributes exactly two consecutive elements directly inside this grid
   (see renderModeB() in app.js — no per-pair wrapper), CSS Grid's
   auto-placement is what actually aligns each translation with its own
   original on the same row; nothing here computes rows or columns by hand. */

#segments.mode-b {
  display: grid;
  grid-template-columns: 1fr; /* portrait/mobile default: one column */
  gap: 4px 24px;
  align-items: start;
  padding: 16px;
  /* Same reasoning as the Mode A #segments rule above: #controls is
     position:fixed and no longer reserves space at the top, so this box's
     top edge is the real top of the viewport. */
  padding-top: calc(16px + env(safe-area-inset-top));
  padding-left: calc(16px + env(safe-area-inset-left));
  padding-right: calc(16px + env(safe-area-inset-right));
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
  min-height: 100dvh;
  /* No `overflow` set here on purpose, same as Mode A's #segments rule:
     once pairs accumulate past one screenful, this grid grows past
     100dvh and the PAGE scrolls, not this element — see app.js's
     scrollToBottomIfSticking(). */
  overscroll-behavior: none;
}

.segment-original,
.segment-translation {
  margin: 0;
  opacity: 1;
  /* Same reasoning as .segment's overflow-wrap above — Mode B's grid has
     the same "vertical scroll only" assumption. */
  overflow-wrap: anywhere;
}

/* Original: muted, ~70% of the translation's size — for checking what ASR
   heard, not for reading (plan 9.3). */
.segment-original {
  color: #999;
  font-size: calc(var(--font-size) * 0.7);
  /* Shares --segment-line-height with Mode A: same "fit more without
     shrinking the font" reasoning applies to paired mode too, since a
     pair already costs roughly 1.7x a single Mode A line (original +
     translation) at any given segment count. */
  line-height: var(--segment-line-height);
}

/* Translation: the main text, full size, white. */
.segment-translation {
  color: #fff;
  font-size: var(--font-size);
  line-height: var(--segment-line-height);
}

/* Portrait: a thin line between PAIRS (not columns — there are none in
   portrait). Targets the first element of every pair after the first
   (nth-child(odd) = first-of-pair, regardless of which role — original or
   translation — happens to be first per the column-order toggle). */
#segments.mode-b > :nth-child(odd):not(:first-child) {
  border-top: 1px solid #333;
  padding-top: 8px;
  margin-top: 4px;
}

@media (orientation: landscape) {
  #segments.mode-b {
    grid-template-columns: 1fr 1fr;
  }

  /* In landscape the pair-separator becomes a column-separator instead —
     a thin vertical line between the two columns, on whichever element
     auto-placement put in column 1 (again: first-of-pair, role-agnostic). */
  #segments.mode-b > :nth-child(odd):not(:first-child) {
    border-top: none;
    padding-top: 0;
    margin-top: 0;
  }
  #segments.mode-b > :nth-child(odd) {
    border-right: 1px solid #444;
    padding-right: 16px;
  }
}

/* One-time "rotate your phone" hint (plan 9.3). Only ever inserted into
   the DOM once per app lifetime (see app.js maybeShowRotateHint) — once
   there, whether it's visible at any given moment is decided purely by
   this orientation query, with zero further JS involvement. */
#rotate-hint {
  position: fixed;
  left: calc(16px + env(safe-area-inset-left));
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  padding: 12px 16px;
  background: #222;
  color: #fff;
  border: 1px solid #555;
  border-radius: 8px;
  font-size: 16px;
  text-align: center;
  display: none;
}

@media (orientation: portrait) {
  #rotate-hint {
    display: block;
  }
}

/* Colors/border come from Pico's "outline" button class on the markup;
   only the sizing/tap-target is set here. */
#mode-controls button {
  font-size: 16px;
  min-height: 44px;
  padding: 0 12px;
}

/* --- Desktop upscale ------------------------------------------------------
   768px is a conventional, easy-to-remember boundary: it's comfortably
   past the widest common phones (including landscape), and roughly where
   tablets/small laptops start — i.e. "no longer a phone in hand". The plan
   doesn't mandate a specific number, just that this direction (min-width,
   mobile as the base) is the only one used. */
@media (min-width: 768px) {
  :root {
    --font-size: 48px;
    /* Desktop isn't fighting for vertical space the way a phone is —
       give the gap back (was a flat 12px before the mobile-first
       --segment-gap split above) rather than keeping the cramped mobile
       value everywhere. */
    --segment-gap: 16px;
  }
}

/* --- Pre-login gating (see index.html/app.js) -----------------------------
   These elements start with the `hidden` attribute and app.js removes it
   once a session is confirmed. The plain `[hidden] { display: none }` UA
   rule alone isn't enough here: several of the ID selectors above
   (`#capture-controls`, `#segments`, `#segments.mode-b`, ...) are MORE
   specific than a bare attribute selector and would win instead, leaving
   the element visible despite `hidden`. This block is deliberately last in
   the file and uses ID+attribute (and ID+class+attribute for Mode B)
   selectors so it always wins on both specificity and source order,
   regardless of which display-mode class happens to be set. */
#capture-controls[hidden],
#font-size-controls[hidden],
#mode-controls[hidden],
#segments[hidden],
#segments.mode-b[hidden] {
  display: none;
}
