/* SURI Maze — STEP 2
   All colors and node shapes live here as CSS variables so a later dark mode
   can redefine them in one place. No dark mode yet. */

:root {
  --bg: #fbfaf8;
  --text: #2e2c29;
  --text-quiet: #6b6761;
  --text-faint: #97918a;
  --line: #e2ded7;
  --field-bg: #ffffff;
  --accent: #3f3b35;
  --accent-text: #fbfaf8;
  --focus: #9c968c;

  /* Maze */
  --node-bg: #ffffff;
  --node-border: #ddd8d0;
  --node-text: #2e2c29;
  --node-center-bg: #3f3b35;
  --node-center-border: #3f3b35;
  --node-center-text: #fbfaf8;
  --connector: #e6e1d9;

  /* docs/03 "three (plus closed) levels" — text color is the only signal
     of a node's origin; background/border stay the same for every
     non-center node regardless of tier. */
  --tone-authored: #2e2c29;  /* the user's own words: user_edited/user_added, and accepted */
  --tone-extracted: #5c574f; /* AI's faithful extraction (ai_extracted) */
  --tone-suggested: #8a847c; /* an untouched AI candidate (ai_suggested) */
  --tone-closed: #bdb7ad;    /* a closed branch — faintest, just enough to read */

  --font: -apple-system, BlinkMacSystemFont, "Hiragino Sans",
          "Hiragino Kaku Gothic ProN", "Noto Sans JP", "Yu Gothic Medium",
          "Yu Gothic", Meiryo, sans-serif;

  --page-gutter: 16px;
  --measure: 34rem;
  --tap: 44px;
  --node-w: 96px;
  --node-center-w: 92px;
  --node-font: 0.75rem;
  --node-center-font: 0.8rem;

  /* How long the tapped node takes to travel to the center and morph into
     a center node's look — read by both app.js (to time the deferred
     navigation) and the .is-arriving transition below. */
  --focus-move-ms: 900;

  /* Brief crossfade for any screen-level navigation (.app-fade.is-switching)
     — unrelated to the travel above, just enough to avoid a hard cut. */
  --stage-switch-ms: 180;

  /* FOCUS entrance timing — the one place to retune it. --enter-child-ms/
     --enter-stagger-ms/--enter-start-delay-ms are unitless ms, read as
     plain numbers by app.js. --enter-travel and --enter-start-scale are
     used only inside the CSS keyframe below (app.js never reads them):
     --enter-travel is the fraction of the full center-to-destination
     distance a candidate actually travels — 0.25 starts it 75% of the way
     there already; 0 makes it a pure fade with no movement at all. */
  --enter-child-ms: 900;
  --enter-stagger-ms: 120;
  --enter-start-delay-ms: 120;
  --enter-travel: 0.25;
  --enter-start-scale: 0.88;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

body, #root {
  min-height: 100vh;
  /* dvh keeps the layout stable when iOS Safari's bars show and hide */
  min-height: 100dvh;
}

.screen {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding:
    calc(env(safe-area-inset-top) + 40px)
    calc(env(safe-area-inset-right) + var(--page-gutter))
    calc(env(safe-area-inset-bottom) + 28px)
    calc(env(safe-area-inset-left) + var(--page-gutter));
}

.screen--maze { padding-top: calc(env(safe-area-inset-top) + 16px); }

.inner {
  width: 100%;
  max-width: var(--measure);
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Wraps whatever screen is currently shown (PLACE, END, TAKE, ONE CARD,
   the Maze itself, ...) — one crossfade mechanism for every screen-level
   navigation (switchView, in app.js), not just the Maze's own back/whole. */
.app-fade {
  opacity: 1;
  transition: opacity calc(var(--stage-switch-ms) * 1ms) ease-out;
}

.app-fade.is-switching { opacity: 0; }

/* --- 01 PLACE --- */

.prompt {
  margin: 0 0 10px;
  font-size: 1rem;
  line-height: 2;
  letter-spacing: 0.02em;
}

.prompt span { display: block; }

.prompt-sub {
  margin: 0 0 22px;
  font-size: 0.8rem;
  line-height: 1.8;
  color: var(--text-faint);
}

.field-wrap {
  position: relative;
  flex: 1;
  display: flex;
  min-height: 36vh;
}

.field {
  flex: 1;
  width: 100%;
  resize: none;
  padding: 18px 18px 64px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--field-bg);
  color: var(--text);
  /* 16px minimum stops iOS Safari from zooming in on focus */
  font-size: 1rem;
  font-family: inherit;
  line-height: 1.9;
  outline: none;
}

.field:focus { border-color: var(--focus); }

.mic {
  position: absolute;
  right: 10px;
  bottom: 10px;
  min-height: var(--tap);
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--field-bg);
  color: var(--text-quiet);
  font-family: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.mic.is-listening {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

.counter {
  margin: 8px 2px 0;
  text-align: right;
  font-size: 0.75rem;
  color: var(--text-faint);
}

.mic-note {
  margin: 8px 2px 0;
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--text-faint);
}

.actions {
  margin-top: 24px;
  display: flex;
  justify-content: center;
}

/* docs/03 §01 PLACE — the same faint, quiet register as .mic-note, shown
   below the button rather than replacing it. */
.open-hint {
  margin: 10px 2px 0;
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--text-faint);
  text-align: center;
}

.map-error {
  margin: 10px 2px 0;
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--text-faint);
  text-align: center;
}

.resume-row {
  margin-top: 14px;
  display: flex;
  justify-content: center;
}

.button-primary {
  width: 100%;
  min-height: var(--tap);
  padding: 16px 24px;
  border: none;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-text);
  font-family: inherit;
  font-size: 1rem;
  letter-spacing: 0.06em;
  cursor: pointer;
}

.button-primary:disabled { opacity: 0.3; cursor: default; }

/* --- navigation --- */

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: var(--tap);
}

.nav--foot { justify-content: center; gap: 28px; margin-top: 8px; }

.button-quiet {
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
  border: none;
  background: none;
  color: var(--text-quiet);
  font-family: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}

/* Always in the DOM (never conditionally mounted) so its slot in the nav
   row never appears or disappears — only its own opacity changes, which
   cannot shift anything else's layout. Fades in exactly when a FOCUS view
   becomes current: on arrival, or on 戻る/Maze全体を見る landing on one. */
.nav-whole {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease-out;
}

.nav-whole.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* Same reasoning as .nav-whole: always rendered with its real two lines of
   text so the space is reserved on every screen (MAP included, empty),
   and only opacity — never display/height — changes. */
.guidance {
  margin: 4px 0 0;
  font-size: 0.85rem;
  line-height: 1.9;
  color: var(--text-quiet);
  opacity: 0;
  transition: opacity 0.3s ease-out;
}

.guidance.is-visible { opacity: 1; }

.guidance span { display: block; }

/* --- Maze stage --- */

/* The flex child that actually reserves the stage's box; .stage below
   only fills it. Kept separate from .app-fade's crossfade so the two
   never compound: this is purely a positioning context. */
.stage-wrap {
  position: relative;
  flex: 1;
  min-height: 52vh;
  margin: 8px 0;
}

.stage {
  /* absolute+inset, not width/height:100%: a block child's percentage
     height needs the parent's height to be treated as definite, and that
     did not reliably hold through the .stage-wrap flex/min-height chain
     (it measured 0 in testing). inset:0 against the positioned
     .stage-wrap has no such ambiguity. */
  position: absolute;
  inset: 0;
}

.stage-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.stage-lines line {
  stroke: var(--connector);
  stroke-width: 1;
}

.node {
  position: absolute;
  transform: translate(-50%, -50%);
  /* An explicit width, not max-width: an absolutely positioned box would
     otherwise shrink to the space between its `left` and the stage edge,
     so nodes on the right would collapse into a narrow column. */
  width: var(--node-w);
  min-height: var(--tap);
  padding: 8px;
  border: 1px solid var(--node-border);
  border-radius: 12px;
  background: var(--node-bg);
  color: var(--node-text);
  font-family: inherit;
  font-size: var(--node-font);
  line-height: 1.55;
  text-align: center;
  word-break: auto-phrase;
  cursor: pointer;
  /* Ambient, low-key transition for ordinary state changes that are not
     one of the deliberate animations below: a node's ring position
     easing over when a hidden sibling's removal reflows the rest, a tone
     color easing in on 近い / 言葉を直す, and the border appearing or
     disappearing as a node goes OFF (違う) or comes back (戻す). Harmless
     everywhere else, since nothing here changes these properties outside
     those cases, and a running `animation`
     (is-arriving/is-entering/is-leaving/is-hiding) always takes priority
     over a `transition` for the same property. */
  transition: left 0.25s ease-out, top 0.25s ease-out,
    color 0.25s ease-out, border-color 0.25s ease-out;
}

.node--center {
  width: var(--node-center-w);
  background: var(--node-center-bg);
  border-color: var(--node-center-border);
  color: var(--node-center-text);
  font-size: var(--node-center-font);
  cursor: default;
}

.tone--authored { color: var(--tone-authored); }
.tone--extracted { color: var(--tone-extracted); }
.tone--suggested { color: var(--tone-suggested); }
.tone--closed { color: var(--tone-closed); }

/* OFF (違う, before 消す) reads as faint as .tone--closed but with no
   border — that's the one difference between the two at a glance. */
.node.is-off { border-color: transparent; }

/* The "＋自分の言葉" invitation Stage draws in place of real candidates
   when a focus has none (docs/03 §03 EXPLORE) — dashed, so it reads as an
   opening rather than a candidate. Not a real node: no tone class, no
   source, just this one look regardless of where Stage places it. */
.node--empty-petal {
  border-style: dashed;
  color: var(--tone-suggested);
}

@keyframes petalIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.node--empty-petal.is-entering {
  animation: petalIn 0.4s ease-out both;
}

/* --- FOCUS transition ---
   Two separate motions, run in sequence rather than together:

   1. The tapped node itself travels from its candidate spot to the
      center, morphing size/fill/border/text color into a center node's
      look as it goes (.is-arriving). It is the SAME element throughout —
      app.js renders it from a single id-keyed list whether it currently
      counts as "a child" or "the center", so this is a plain CSS
      *transition* on an element that keeps existing, not a mount-time
      keyframe: the browser interpolates from whatever it was already
      showing a moment ago, so no captured "from" values are needed here.
      Everything else left over from the previous screen — the old
      center, the other candidates, the connecting lines — fades away on
      its own, faster, starting at the same instant (.is-leaving).
   2. Only once that settles (transitionend) does app.js actually
      navigate to the new FOCUS view, mounting the candidates' own
      "materialize" entrance (.is-entering, below) — so no timing math is
      needed here to keep the two apart; reacting to the real transition
      finishing does that for free, with no drift against a guessed
      duration.

   left/top are animated directly, not via percentage transform:
   percentages in `transform: translate()` are relative to the element's
   own box, not the stage, so the true stage-relative offsets have to
   travel through left/top instead. */

.node.is-arriving {
  transition:
    left calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    top calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    width calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    background-color calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    border-color calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    color calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1),
    font-size calc(var(--focus-move-ms) * 1ms) cubic-bezier(0.45, 0, 0.2, 1);
  z-index: 1;
}

@keyframes fadeAway {
  from { opacity: 1; }
  to   { opacity: 0; }
}

.node.is-leaving,
.stage-lines.is-leaving line {
  animation: fadeAway 0.4s ease-out both;
}

/* 違う: fades out in its ring slot rather than vanishing instantly; app.js
   keeps it in the list for exactly this long, then drops it, at which
   point the ambient .node transition above quietly reflows whatever is
   left into the now-smaller ring. */
.node.is-hiding {
  animation: fadeAway 0.3s ease-out both;
}

@keyframes nodeEnterChild {
  from {
    left: calc(50% + var(--dx, 0) * 1% * (1 - var(--enter-travel)));
    top: calc(50% + var(--dy, 0) * 1% * (1 - var(--enter-travel)));
    transform: translate(-50%, -50%) scale(var(--enter-start-scale));
    opacity: 0;
  }
  /* Opacity finishes early and holds; position/scale keep easing to 100%. */
  70% {
    opacity: 1;
  }
  to {
    left: var(--final-left, 50%);
    top: var(--final-top, 50%);
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
}

@keyframes lineEnter {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* :not(.node--empty-petal) matters, not just tidiness: the petal has no
   --dx/--final-left/etc, so if it matched this rule too, nodeEnterChild
   would fall back those custom properties to their defaults (0 / 50%)
   and animate it to dead center regardless of the inline position below —
   confirmed in testing, where that is exactly what happened. */
.node:not(.node--center):not(.node--empty-petal).is-entering {
  animation: nodeEnterChild calc(var(--enter-child-ms) * 1ms)
    cubic-bezier(0.45, 0, 0.2, 1) both;
}

.stage-lines.is-entering line {
  opacity: 0;
  animation: lineEnter 0.18s ease-out both;
}

/* --- node menu / edit sheet ---
   Shared bottom-sheet shell (.sheet-overlay > .sheet or .edit-sheet) for
   both the node menu (docs/03 "Node actions") and the edit screen
   (言葉を直す / ＋自分の言葉). Text only, no emoji, no color-coded icons —
   the menu is read the same way regardless of which node opened it. */

.sheet-overlay {
  position: fixed;
  inset: 0;
  background: rgba(46, 44, 41, 0.32);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 10;
  animation: overlayIn 0.18s ease-out both;
}

@keyframes overlayIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes sheetIn {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.sheet {
  width: 100%;
  max-width: var(--measure);
  background: var(--field-bg);
  border-radius: 16px 16px 0 0;
  padding: 8px 4px calc(env(safe-area-inset-bottom) + 8px);
  animation: sheetIn 0.22s ease-out both;
}

.sheet-note {
  margin: 10px 16px 2px;
  font-size: 0.8rem;
  color: var(--text-faint);
}

.sheet-item {
  display: block;
  width: 100%;
  text-align: left;
  min-height: var(--tap);
  padding: 0 16px;
  border: none;
  border-top: 1px solid var(--line);
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
  cursor: pointer;
}

.sheet-item:first-child { border-top: none; }

.edit-sheet {
  width: 100%;
  max-width: var(--measure);
  background: var(--field-bg);
  border-radius: 16px 16px 0 0;
  padding: 20px 20px calc(env(safe-area-inset-bottom) + 20px);
  animation: sheetIn 0.22s ease-out both;
}

.edit-input {
  width: 100%;
  min-height: var(--tap);
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #ffffff;
  color: var(--text);
  /* 16px minimum stops iOS Safari from zooming in on focus */
  font-size: 1rem;
  font-family: inherit;
  outline: none;
}

.edit-input:focus { border-color: var(--focus); }
.edit-input::placeholder { color: var(--text-faint); }

.edit-actions {
  display: flex;
  gap: 12px;
  margin-top: 16px;
}

.edit-actions .button-primary { flex: 1; width: auto; }

.edit-cancel {
  flex: 1;
  min-height: var(--tap);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: none;
  color: var(--text-quiet);
  font-family: inherit;
  font-size: 1rem;
  cursor: pointer;
}

/* --- END --- */

.end-message {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 1.15rem;
  line-height: 1.9;
  margin: 0;
}

.end-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  margin-bottom: 12vh;
}

/* --- TAKE --- */

.take-prompt {
  margin: 32px 0 28px;
  font-size: 1.05rem;
  line-height: 1.9;
  text-align: center;
}

.take-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 28px;
}

.take-item {
  min-height: var(--tap);
  padding: 14px 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--field-bg);
  font-family: inherit;
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
}

.take-own {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 8px;
}

.take-own .edit-input { width: 100%; }
.take-own .button-primary { width: 100%; }

/* --- ONE CARD --- */

.card {
  position: relative;
  margin: 10vh auto 8vh;
  padding: 64px 40px;
  max-width: 26rem;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 0;
  background: var(--bg);
  text-align: center;
}

/* The double-line frame: the outer line is .card's own border above; this
   is the second, thinner line a little further in — matching the exported
   Canvas image (drawCardCanvas in app.js), which draws the same two
   rectangles. */
.card::before {
  content: '';
  position: absolute;
  inset: 10px;
  border: 1px solid var(--line);
  pointer-events: none;
}

.card-label {
  margin: 0 0 40px;
  font-size: 0.7rem;
  letter-spacing: 0.3em;
  color: var(--text-faint);
}

.card-text {
  margin: 0 0 40px;
  font-size: 1.3rem;
  line-height: 1.9;
  color: var(--text);
}

.card-date {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  color: var(--text-faint);
}

/* Only ever mounted on iPhone/iPad Safari (see OneCardScreen in app.js),
   and only after 画像として保存 has been pressed — fades in quietly rather
   than appearing with a hard cut. */
.card-save-hint {
  margin: 10px 0 0;
  font-size: 0.78rem;
  line-height: 1.7;
  color: var(--text-faint);
  text-align: center;
  opacity: 0;
  transition: opacity 0.4s ease-out;
}

.card-save-hint.is-visible { opacity: 1; }

.after-work-link {
  display: block;
  margin: 28px auto 0;
  font-size: 0.82rem;
}

/* --- crisis (docs/05 "Crisis route") ---
   Same quiet register as the rest of the app — no red, no alarm styling,
   per docs/05 "Tone" (flat, warm without intrusion). .crisis-inner
   centers the whole thing vertically like .end-message does for END. */

.crisis-inner {
  justify-content: center;
}

.crisis-message {
  margin: 0 0 22px;
  font-size: 1rem;
  line-height: 1.9;
  text-align: center;
}

.crisis-message span,
.crisis-note span { display: block; }

.crisis-link {
  text-decoration: none;
  text-align: center;
}

.crisis-emergency {
  margin: 4px 0 28px;
  font-size: 0.9rem;
  line-height: 1.8;
  text-align: center;
  color: var(--text);
}

.crisis-note {
  margin: 0 0 4px;
  font-size: 0.8rem;
  line-height: 1.7;
  text-align: center;
  color: var(--text-faint);
}

.crisis-inner .actions:last-of-type { margin-top: 28px; }

/* --- placeholder screens --- */

.placeholder-note {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-quiet);
  font-size: 0.95rem;
  line-height: 2;
}

/* --- PC --- */

@media (min-width: 768px) {
  :root {
    --page-gutter: 20px; --node-w: 140px; --node-center-w: 124px;
    --node-font: 0.85rem; --node-center-font: 0.9rem;
  }

  .screen { padding-top: 72px; padding-bottom: 48px; }
  .screen--maze { padding-top: 32px; }

  .field { padding: 22px 22px 68px; }
  .button-primary { width: auto; min-width: 15rem; }

  .node { padding: 10px 14px; }
}

@media (prefers-reduced-motion: reduce) {
  .app-fade { transition: none; }
  .nav-whole,
  .guidance,
  .card-save-hint { transition: none; }
  .node { transition: none; }
  .node.is-arriving { transition: none; }
  .node.is-entering,
  .node.is-leaving,
  .node.is-hiding,
  .stage-lines.is-entering line,
  .stage-lines.is-leaving line { animation: none; }
  .sheet-overlay,
  .sheet,
  .edit-sheet { animation: none; }
}
