/* style.css — diary app-specific styles.
 *
 * Unlayered on purpose: m3e.css puts its components in @layer components, so plain rules here win
 * by cascade-layer precedence without needing !important.
 *
 * Structure of 繪畫 (see index.html):
 *   titlebar  · fixed height, carries ↶↷⌧🗑 in 連續 only
 *   canvas    · TWO canvases in ONE rect, stacked in Z
 *   inputbar  · 一次一則 only, BELOW the canvas
 *   navbar    · shared app nav
 */

/* ---------- rotation gate ----------
 * Shown INSTEAD of the app after a rotation on 繪畫 (app.js showRotationGate): the UI is removed,
 * not covered, so §6.1's "nothing may ever sit on top of the drawing canvas" is not violated — by
 * the time this exists, the canvas does not. It is the whole document body, hence the plain
 * centred block rather than an overlay: no z-index, nothing to stack against.
 * Uses the theme roles so it is legible in 淺色/深色/純黑 alike.
 */
.rotgate {
  min-height: 100dvh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 14px; padding: 24px; box-sizing: border-box; text-align: center;
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  font-family: var(--font);
}
.rotgate-t { margin: 0; font-size: 20px; font-weight: 600; }
.rotgate-s { margin: 0; max-width: 34ch; line-height: 1.6; opacity: .8; }
/* The lost-work line is the one thing here the user did not choose; give it the error role. */
.rotgate-w { margin: 0; max-width: 34ch; line-height: 1.6; color: var(--md-sys-color-error); }
.rotgate-b {
  margin-top: 8px; padding: 12px 28px; border: 0; border-radius: 999px;
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  font: inherit; font-size: 16px; cursor: pointer;
}

/* ---------- app frame ---------- */
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  font-family: var(--font);
  overscroll-behavior: none;
}
.app { display: flex; min-height: 100dvh; }
.appmain {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* leave room for the bottom navbar + the notch */
  padding-bottom: calc(var(--tr-navbar-h, 64px) + env(safe-area-inset-bottom));
}
.appmain > .view.active { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
@media (min-width: 600px) {
  .appmain { padding-bottom: 0; padding-left: var(--rail-w, 80px); }
}

.ico { width: 1.4em; height: 1.4em; display: inline-block; vertical-align: middle; fill: none; flex: 0 0 auto; }

/* ---------- titlebar ---------- */
.titlebar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px calc(env(safe-area-inset-right) + 14px) 10px calc(env(safe-area-inset-left) + 14px);
  padding-top: calc(env(safe-area-inset-top) + 10px);
  color: var(--md-sys-color-on-surface);
}
.tb-icon { display: inline-flex; color: var(--md-sys-color-primary); }
/* The 繪畫 book icon is a BUTTON (the minimal-mode toggle) — strip chrome, keep the icon look. */
button.tb-icon { border: 0; background: none; padding: 0; cursor: pointer; }
button.tb-icon[aria-pressed="true"] { color: var(--md-sys-color-tertiary); }

/* ---------- minimal mode ----------
   Fullscreen + no nav chrome; toggled by the titlebar book icon (app.js setMinimal). Hiding the
   nav must also drop .appmain's reserved padding on BOTH layouts, or minimal mode trades chrome
   for dead space. .navpill covers Liquid Glass's floating-pill restyle of the navbar. */
body[data-minimal] .navbar,
body[data-minimal] .navrail,
body[data-minimal] .navpill { display: none; }
body[data-minimal] .appmain { padding-bottom: 0; padding-left: 0; }
.tb-title { margin: 0; font-size: 1.05rem; }
.tb-space { flex: 1 1 auto; }
.tb-ops { display: flex; gap: 2px; }

/* ---------- the canvas stack (繪畫) ----------
   The reply layer and the drawing canvas occupy the SAME rect. The drawing canvas is on top and
   TRANSPARENT so the reply animating beneath shows through — that transparency is structural.
   Never give #cvDraw a background, and never let the reply layer take pointer events. */
.canvas-stack {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 calc(env(safe-area-inset-right) + 12px) 0 calc(env(safe-area-inset-left) + 12px);
  border-radius: 14px;
  overflow: hidden;
  background: var(--md-sys-color-surface);
}
.canvas-stack canvas,
.canvas-stack > #replyLayer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
/* A DIV, not a canvas: Vara renders handwriting as animated SVG stroke paths into a DOM
   container. pointer-events:none is structural — nothing may ever sit on top of the drawing
   canvas (HANDOFF §6.1). overflow:hidden backs the shrink-to-fit rule: this layer must NEVER
   scroll, because it sits under a canvas that must not scroll — fitReply() scales BOTH
   presentations to fit, or overflow:hidden would silently truncate them. `color` is INHERITED from
   body (--md-sys-color-on-surface) and the strokes are recoloured to currentColor, so the ink
   follows a scheme change with no re-render. */
#replyLayer {
  z-index: 0;
  pointer-events: none;
  background: transparent;
  overflow: hidden;
  padding: 12px;
  box-sizing: border-box;
}
/* The fade presentation is plain text, so it needs a real font — Satisfy, the SAME self-hosted
   typeface Vara draws live and 歷史 renders in (.hand), so all three surfaces are one hand.
   Vara itself needs none: it draws paths and ignores fonts entirely.
   NOT the old ui-rounded/"Segoe Script"/"Bradley Hand" stack — Apple and Windows fonts listed on
   an ANDROID tablet, where every one missed and it fell through to generic `cursive`, which is a
   different hand from the live surface however it resolves. This is not a corner case: fade is a
   shipped 設定 choice (逐字浮現) AND the automatic fallback on every Vara-failure path.
   NOT italic either: Satisfy is already a script face, so a synthetic oblique smears it (same
   reason .hand dropped it).
   `cursive` trails only for raw public/ serving, where /res/ — and thus BOTH Vara and Satisfy —
   is absent. */
.reply-fade {
  font: 30px/1.5 "Satisfy", cursive;
  color: currentColor;
}
/* NO transition on the spans: renderFade's rAF already interpolates opacity every frame, so a
   transition would re-trigger 60×/s per span and chase a target that has already moved — pure
   compositor churn on the exact weak tablet this mode exists for. */
#cvDraw {
  z-index: 1;
  background: transparent;      /* structural — the reply layer shows through */
  touch-action: none;           /* required: we handle all pointer input ourselves */
  cursor: crosshair;
}
#cvDraw.erase { cursor: cell; }
/* 連續/靜默: the surface is the theme's plain paper — white in 淺色, black in 深色/純黑. The ink is
   on-surface, so contrast is automatic. Both canvas-only modes take the full bleed. */
body[data-mode="continuous"] .canvas-stack,
body[data-mode="silent"] .canvas-stack { border-radius: 0; margin: 0; }

/* ---------- input bar (一次一則 only) ---------- */
.inputbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px calc(env(safe-area-inset-right) + 12px) 10px calc(env(safe-area-inset-left) + 12px);
}
.inputbar.hidden { display: none; }
#ta { flex: 1 1 auto; resize: none; max-height: 120px; }
.pen-tools { flex: 1 1 auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pen-tools.hidden { display: none; }
.swatches { display: flex; gap: 6px; }
.swatch {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px var(--md-sys-color-outline);
  padding: 0;
}
.swatch[aria-pressed="true"] { border-color: var(--md-sys-color-primary); transform: scale(1.1); }
.widths button { display: inline-flex; align-items: center; justify-content: center; width: 34px; }
.dot { display: block; border-radius: 50%; background: currentColor; }
.dot-s { width: 4px; height: 4px; }
.dot-m { width: 7px; height: 7px; }
.dot-l { width: 11px; height: 11px; }
.ops { display: flex; gap: 2px; margin-left: auto; }
.send {
  width: 44px; height: 44px; border-radius: 50%; flex: 0 0 auto;
  background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary);
  display: inline-flex; align-items: center; justify-content: center;
  border: none; cursor: pointer;
}
.send:disabled { opacity: .38; pointer-events: none; }

/* ---------- 歷史 ----------
   Newest at the BOTTOM, scroll up for older. Each entry's PNG renders on a FIXED neutral card,
   never on the theme background: the ink colour is baked into the PNG at capture and the vectors
   are gone, so painting a dark-ink entry straight onto 純黑 would make it invisible. The card is
   the album-page that keeps every entry legible in any theme. */
.hist {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 8px calc(env(safe-area-inset-right) + 14px) 20px calc(env(safe-area-inset-left) + 14px);
}
.hist-empty { text-align: center; padding: 24px; }
.hist-empty.hidden { display: none; }
.entry { display: flex; flex-direction: column; gap: 6px; }
.entry.mine { align-items: flex-end; }
.entry-when { font-size: .72rem; color: var(--md-sys-color-on-surface-variant); padding: 0 4px;
  display: flex; align-items: center; gap: 8px; }
/* The retract mark on own entries — deliberately quiet, error-tinted on press targets only. */
.ig-btn { border: 0; background: none; padding: 0 4px; cursor: pointer; font-size: .8rem;
  color: var(--md-sys-color-on-surface-variant); }
.ig-btn:hover, .ig-btn:focus-visible { color: var(--md-sys-color-error); }
.paper {
  --paper-bg: #f4f1ea;                 /* FIXED, theme-independent — see note above */
  background: var(--paper-bg);
  border-radius: 12px;
  padding: 10px;
  max-width: min(88%, 520px);
  box-shadow: var(--e1);
}
.paper img, .paper svg { display: block; max-width: 100%; height: auto; }
/* The LLM's reply renders as STATIC text on the same paper card, so the archive reads as one album
   (David, 2026-07-17: "just render the reply text from LLM for history page"). Only the USER's
   drawings are PNGs — for those the PNG is the only surviving artifact (§6.4: vectors are discarded
   after the crop), whereas a reply's text is never lost, so rasterising it buys nothing.
   The card stays a FIXED neutral (§6.5) because PNG ink is baked at capture: a dark-ink drawing
   painted onto 純黑 would be invisible. That mitigation is required, not optional.
   The hand is **Satisfy**, self-hosted — the SAME typeface Vara draws on the live surface (its
   stroke JSON is /res/vara/1.4.1/fonts/Satisfy/SatisfySL.json, this is the outline webfont), so
   HANDOFF §6.5's "same hand as the live surface" holds for real. It replaced a stack of
   ui-rounded/"Segoe Script"/"Bradley Hand" — Apple and Windows fonts, listed on an ANDROID tablet,
   where every one missed and it fell through to generic `cursive`. */
.entry.reply .paper { --paper-bg: #eef1f4; }
.hand {
  /* Satisfy, self-hosted from /res/ — the same typeface Vara draws live. `cursive` trails it only
     as a last resort (serving public/ raw, where /res/ does not exist). NOT italic: Satisfy is
     already a script face, so a synthetic oblique on top of it just smears. */
  font: 20px/1.6 "Satisfy", cursive;
  color: #1c1917;                      /* fixed, like the ink baked into a drawing's PNG */
  white-space: pre-wrap;
  word-break: break-word;
}

/* The binding TXP must render crisp (it is photographed): no smoothing, white surround so the
   quiet zone survives any theme. */
.bind-box { margin-top: 12px; }
.bind-box img { display: block; max-width: min(100%, 360px); image-rendering: pixelated;
  background: #fff; padding: 8px; border-radius: 8px; }
/* A Beam frame is rendered at close to 1:1, so keep the pixels crisp rather than
   letting the browser smooth 57 modules into mush. */
.bind-box canvas { display: block; max-width: min(100%, 360px); height: auto;
  image-rendering: pixelated; background: #fff; padding: 8px; border-radius: 8px; }
.bind-box video { display: block; width: 100%; max-width: min(100%, 360px);
  background: #000; border-radius: 8px; }
.bind-box .seg { margin-bottom: 10px; }
.bind-box.hidden, .bind-box .hidden { display: none; }

/* E-ink refresh flash: fixed pure-B/W plate, 600ms lifetime, never intercepts input. */
.eink-flash { position: fixed; inset: 0; z-index: 9999; pointer-events: none; background: #000; }

/* ---------- 設定 ---------- */
.settings-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 calc(env(safe-area-inset-right) + 14px) 24px calc(env(safe-area-inset-left) + 14px);
}
.kv { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; }
.status { min-height: 1.2em; font-size: .85rem; color: var(--md-sys-color-on-surface-variant); }
.app-version { font-size: .75rem; color: var(--md-sys-color-on-surface-variant); margin: 10px 0 0; }
.btn-flow { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.field { margin-bottom: 20px; }
.field > label { display: block; font-size: .8rem; margin-bottom: 8px; color: var(--md-sys-color-on-surface-variant); }
.dev { margin-top: 12px; }
.seed-dot { width: 30px; height: 30px; border-radius: 50%; border: 2px solid transparent; padding: 0; }
.seed-dot[aria-pressed="true"] { border-color: var(--md-sys-color-on-surface); }

/* ---------- update bar + toast ---------- */
.update-bar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--tr-navbar-h, 64px) + env(safe-area-inset-bottom) + 12px); z-index: 60;
  display: flex; align-items: center; gap: 14px; max-width: 92vw;
  padding: 8px 8px 8px 18px; border-radius: var(--r-full);
  background: var(--md-sys-color-on-surface); color: var(--md-sys-color-surface);
  box-shadow: 0 8px 28px -10px rgb(0 0 0 / .5);
}
@media (min-width: 600px) { .update-bar { bottom: 24px; } }
