/* ============================================================
   LiveQ — app shell / responsive layout.
   The design kit (tokens.css + components.css) owns look & components.
   THIS file owns only layout + responsiveness for the real app.

   Why not screens.css: that file frames surfaces in a fixed 360px
   `.lq-phone` mock for design review. Real students hold 320–440px
   phones (and some open the board on a tablet), so the app shell is
   fluid, mobile-first, and caps its measure on big screens instead of
   stretching to a 1920px line length.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  /* Prevent iOS/WeChat auto-inflating text and breaking the layout */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--lq-bg);
  color: var(--lq-ink);
  font-family: var(--lq-font-body);
  font-size: var(--lq-fs-body);
  line-height: var(--lq-lh-body);
  -webkit-font-smoothing: antialiased;
  /* WeChat's webview can overscroll oddly; keep the page from bouncing sideways */
  overflow-x: hidden;
}

/* ---------- fluid app shell ---------- */
.app {
  width: 100%;
  max-width: 560px;          /* readable measure; centred on tablet/desktop */
  margin-inline: auto;
  min-height: 100dvh;        /* dvh so the WeChat toolbar doesn't clip content */
  display: flex;
  flex-direction: column;
  background: var(--lq-bg);
}

.app__scroll {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--lq-s-4);
  padding: var(--lq-s-4);
  /* Room for the sticky dock (~72px) plus a full gap, so the last element is never
     tucked underneath it at maximum scroll. */
  padding-bottom: calc(var(--lq-s-10) + var(--lq-s-6) + env(safe-area-inset-bottom, 0px));
}

/* Sticky app bar: session identity is always visible while scrolling */
.app__bar {
  position: sticky;
  top: 0;
  z-index: 20;
  padding-top: env(safe-area-inset-top, 0px);
  background: var(--lq-surface);
  border-bottom: 1px solid var(--lq-line);
}

/* Sticky bottom action (Ask the assistant) — the agent is the daily hook, so it must be
   reachable with a thumb without scrolling.

   It is an overlay, so it can sit on top of whatever happens to be scrolled beneath it.
   Verified case: while a student typed their team question, the dock covered the
   textarea. Two mitigations:
     1. generous scroll padding so the last element always clears it;
     2. .app--typing (set by a focus listener) slides the dock away while any field is
        focused. Plain JS rather than :has(), which older WeChat Chromium may lack; if
        the script never runs the dock simply stays put, as before;
     3. scroll-margin-bottom on every field, below. Measured: a browser-initiated scroll
        (scrollIntoView, focus, anchor) bottom-aligns its target, which parked the team
        textarea 62-72px underneath the dock — and the team question is the one input the
        whole adoption problem hangs on. scroll-margin makes the browser leave the dock's
        height clear. It cannot help a student's own manual scroll, which no sticky
        overlay can, but it fixes every scroll the app itself causes. */
.app__dock {
  position: sticky;
  bottom: 0;
  z-index: 20;
  padding: var(--lq-s-3) var(--lq-s-4) calc(var(--lq-s-3) + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, var(--lq-bg) 62%, transparent);
  transition: transform .18s ease, opacity .18s ease;
}
.app--typing .app__dock {
  transform: translateY(120%);
  opacity: 0;
  pointer-events: none;
}

/* Keep dock-height of clearance whenever the browser scrolls a field into view. */
.app__scroll textarea,
.app__scroll input,
.app__scroll select,
.app__scroll button,
.app__scroll .lq-btn,
.app__scroll summary {
  scroll-margin-bottom: calc(var(--lq-s-10) + var(--lq-s-6));
}

/* ---------- small-phone hardening (320–359px: older/mini Androids) ---------- */
@media (max-width: 359px) {
  :root {
    --lq-fs-body: 15px;
    --lq-fs-title: 20px;
    --lq-fs-headline: 25px;
    --lq-fs-display: 30px;
  }
  .app__scroll { padding: var(--lq-s-3); gap: var(--lq-s-3); }
  .lq-tapcard { padding: var(--lq-s-4); }
  /* 2-up choice grids get cramped below 360px — stack them */
  .lq-choices--2, .lq-choices--4 { grid-template-columns: 1fr; }
}

/* ---------- comfortable phones and up ---------- */
@media (min-width: 480px) {
  .app__scroll { padding: var(--lq-s-5); }
}

/* ---------- tablet / desktop: centred card, not a stretched line ---------- */
@media (min-width: 900px) {
  body { padding: var(--lq-s-6) 0; }
  .app {
    min-height: auto;
    border: 1px solid var(--lq-line);
    border-radius: var(--lq-r-2xl);
    box-shadow: var(--lq-shadow-md);
    overflow: hidden;
  }
  .app__bar { border-radius: 0; }
}

/* ---------- landscape phones: reclaim vertical space ---------- */
@media (max-height: 480px) and (orientation: landscape) {
  .app__scroll { gap: var(--lq-s-3); padding: var(--lq-s-3); }
  .lq-liv { --lq-liv-size: 64px; }
}

/* ---------- neutralise the kit's design-review framing ----------
   screens.css frames surfaces in fixed-width mocks (.lq-phone 360px, .lq-dash 1180px)
   for review. The real app is fluid, so those must not constrain anything. Images are
   clamped globally: an unstyled/oversized asset must never overflow and swallow taps
   (a Liv <img> at natural size did exactly that on the chat header). */
img { max-width: 100%; height: auto; }
/* Defensive floor: an inline SVG with a viewBox but no width/height defaults to
   300x150 in CSS and will blow out a flex row. Every icon should carry explicit
   dimensions, but this makes a forgotten one a cosmetic issue, not a broken page. */
svg:not([width]) { width: 1.25em; height: 1.25em; }
.lq-phone, .lq-dash { width: auto; max-width: none; }
.lq-bothead__mark { flex: none; overflow: hidden; }
.lq-bothead__mark img { width: 150%; max-width: 150%; }

/* ---------- app-level bits the kit doesn't cover ---------- */

.stack { display: flex; flex-direction: column; gap: var(--lq-s-3); }
.muted { color: var(--lq-ink-3); font-size: var(--lq-fs-caption); }
.center { text-align: center; }

/* The "you're not alone" reassurance after a lost-tap (private, on-device) */
.solidarity {
  display: flex; align-items: center; gap: var(--lq-s-3);
  padding: var(--lq-s-4);
  border-radius: var(--lq-r-lg);
  background: var(--lq-work-tint);
  color: var(--lq-work-ink);
  font-family: var(--lq-font-display);
  font-weight: var(--lq-w-bold);
  animation: lqRise .4s cubic-bezier(.2,.7,.2,1) both;
}
.solidarity__n {
  font-family: var(--lq-font-mono);
  font-size: var(--lq-fs-headline);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* Fill-up counter (presence only — never shows who hasn't asked) */
.fillup { display: flex; flex-direction: column; gap: 6px; }
.fillup__row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--lq-s-3); }
.fillup__n { font-family: var(--lq-font-display); font-weight: var(--lq-w-black); font-size: var(--lq-fs-title); }
.fillup__track { height: 8px; border-radius: var(--lq-r-pill); background: var(--lq-bg-sunk); overflow: hidden; }
.fillup__fill { height: 100%; background: var(--lq-primary); border-radius: var(--lq-r-pill); transition: width .5s ease; }

/* Team-question form */
.form-field {
  width: 100%;
  min-height: 96px;
  padding: var(--lq-s-4);
  border-radius: var(--lq-r-md);
  border: 1.5px solid var(--lq-line);
  background: var(--lq-surface);
  font-family: var(--lq-font-body);
  font-size: var(--lq-fs-body);   /* ≥16px: stops iOS/WeChat zooming on focus */
  line-height: var(--lq-lh-body);
  color: var(--lq-ink);
  resize: vertical;
}
.form-field:focus-visible { outline: none; box-shadow: var(--lq-focus-ring); border-color: var(--lq-primary); }

.form-select {
  width: 100%;
  min-height: var(--lq-touch-min);
  padding: 0 var(--lq-s-4);
  border-radius: var(--lq-r-md);
  border: 1.5px solid var(--lq-line);
  background: var(--lq-surface);
  font-family: var(--lq-font-body);
  font-size: var(--lq-fs-body);
  color: var(--lq-ink);
}
.form-label {
  display: block; margin-bottom: 6px;
  font-family: var(--lq-font-body); font-size: var(--lq-fs-caption);
  font-weight: var(--lq-w-semi); color: var(--lq-ink-2);
}

/* Three-way choice buttons on the team form */
.threeway { display: grid; gap: var(--lq-s-3); }
.threeway__btn {
  display: flex; align-items: center; gap: var(--lq-s-3);
  width: 100%; min-height: 60px; padding: var(--lq-s-4);
  cursor: pointer; text-align: left;
  border-radius: var(--lq-r-lg);
  border: 1.5px solid var(--lq-line);
  background: var(--lq-surface);
  font-family: var(--lq-font-display); font-weight: var(--lq-w-bold);
  font-size: var(--lq-fs-body); color: var(--lq-ink);
  transition: var(--lq-tap-fade);
  -webkit-tap-highlight-color: transparent;
}
.threeway__btn:active { transform: scale(.99); }
.threeway__btn:focus-visible { outline: none; box-shadow: var(--lq-focus-ring); }
.threeway__btn small {
  display: block; font-family: var(--lq-font-body); font-weight: var(--lq-w-reg);
  font-size: var(--lq-fs-caption); color: var(--lq-ink-3); margin-top: 2px;
}
.threeway__ico { flex: none; width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center; font-size: 20px; }

/* htmx: show a subtle busy state without layout shift */
.htmx-request { opacity: .72; transition: opacity .15s ease; }
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-flex; }

/* Chat page */
.chat { display: flex; flex-direction: column; min-height: 100dvh; }
.chat__thread {
  flex: 1; display: flex; flex-direction: column; gap: var(--lq-s-4);
  padding: var(--lq-s-4);
  padding-bottom: calc(var(--lq-s-6) + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
}
.chat__form {
  position: sticky; bottom: 0;
  display: flex; align-items: center; gap: var(--lq-s-2);
  padding: var(--lq-s-3) var(--lq-s-4) calc(var(--lq-s-3) + env(safe-area-inset-bottom, 0px));
  background: var(--lq-surface); border-top: 1px solid var(--lq-line);
}
.chat__input {
  flex: 1; min-height: 44px; padding: 0 var(--lq-s-4);
  border-radius: var(--lq-r-pill); border: 1.5px solid var(--lq-line);
  background: var(--lq-bg); font-family: var(--lq-font-body);
  font-size: var(--lq-fs-body);      /* ≥16px prevents focus-zoom */
  color: var(--lq-ink);
}
.chat__input:focus-visible { outline: none; box-shadow: var(--lq-focus-ring); }
.chat__send {
  flex: none; width: 44px; height: 44px; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  color: #fff; background: var(--lq-primary);
}

/* Research/privacy notice — informative, not a consent wall */
.privacy { border-top: 1px solid var(--lq-line); padding-top: var(--lq-s-3); }
.privacy__sum {
  cursor: pointer; list-style: none;
  font-family: var(--lq-font-body); font-size: var(--lq-fs-caption);
  color: var(--lq-ink-3); text-decoration: underline; text-underline-offset: 3px;
  min-height: var(--lq-touch-min); display: flex; align-items: center;
}
.privacy__sum::-webkit-details-marker { display: none; }
.privacy__body { font-size: var(--lq-fs-caption); color: var(--lq-ink-2); line-height: 1.5; }
.privacy__body p { margin: 0 0 var(--lq-s-3); }
.privacy__zh { color: var(--lq-ink-3); font-size: var(--lq-fs-micro); }

/* Prose inside answers: paragraphs, lists and emphasis, sized for a phone.
   Without this every answer rendered as one unbroken wall of text. */
.rich > :first-child { margin-top: 0; }
.rich > :last-child { margin-bottom: 0; }
.rich p { margin: 0 0 .7em; }
.rich ul, .rich ol { margin: .35em 0 .7em; padding-left: 1.35em; }
.rich li { margin-bottom: .3em; }
.rich li::marker { color: var(--lq-ink-3); }
.rich strong { font-weight: var(--lq-w-bold); color: var(--lq-ink); }
.rich code {
  font-family: var(--lq-font-mono); font-size: .92em;
  padding: 1px 5px; border-radius: 5px; background: var(--lq-bg-sunk);
}
/* FAQ answers get a touch more room to breathe than the compact kit default */
.lq-faq__a.rich { font-size: var(--lq-fs-body); line-height: var(--lq-lh-body); }

.form-help { display: block; margin-top: 5px; font-size: var(--lq-fs-micro); color: var(--lq-ink-3); line-height: 1.35; }

/* Board ↔ assistant navigation.
   Full 44px touch target (the old ghost button was 36px, under our own minimum) and a
   label beside the icon, because icon-only navigation is a comprehension risk for an
   EMI cohort and "home" has no obvious meaning in this app. */
.chatnav {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  min-height: var(--lq-touch-min); padding: 0 var(--lq-s-4);
  border-radius: var(--lq-r-pill);
  border: 1.5px solid var(--lq-line); background: var(--lq-surface);
  color: var(--lq-ink); text-decoration: none;
  font-family: var(--lq-font-display); font-weight: var(--lq-w-bold);
  font-size: var(--lq-fs-caption); white-space: nowrap;
  transition: var(--lq-tap-fade); -webkit-tap-highlight-color: transparent;
}
.chatnav:active { transform: translateY(1px); }
.chatnav:focus-visible { outline: none; box-shadow: var(--lq-focus-ring); }
.chatnav--muted { color: var(--lq-ink-2); border-style: dashed; }
/* On the narrowest phones the bot header is tight — keep the icon, drop the word */
@media (max-width: 359px) {
  .chatnav span { display: none; }
  .chatnav { padding: 0 var(--lq-s-3); }
}
