/* screen: trip-train
   Polish pass (2026-08-12): Fuji visibility, vote chip, warm interior
   recomposition (window/interior split, blue banquette, traveler silhouette),
   route band moved to its own footer strip, idle timer, .flag component. */

.trip-train { display: flex; flex-direction: column; gap: 16px; }

/* ---------- head ---------- */

.trip-train-head { display: flex; flex-direction: column; gap: 6px; }
.trip-train-title { font-size: clamp(20px, 2.6vw, 26px); letter-spacing: -.3px; }
.trip-train-title-arrow { color: var(--muted); font-weight: 700; margin: 0 2px; }
.trip-train-sub { color: var(--ink-soft); font-weight: 700; font-size: 14px; }

/* ---------- scene tokens ---------- */

.trip-train-scene {
  --scene-sky-top: #FFB199;
  --scene-sky-mid: #FFD9CE;
  --scene-sky-bottom: #FCEBE4;
  --scene-sun: #FFE8A3;
  --scene-fuji: #7E93B8;
  --scene-fuji-2: #5E7396;
  --scene-snow: #FFFFFF;
  --scene-hill-back: #A9C89A;
  --scene-hill-front: #8FB87D;
  --scene-wood: #6B4226;
  --scene-wood-trim: #8A5A34;
  --scene-wood-light: #A9754A;
  --scene-bench-a: #4C6FA0;
  --scene-bench-b: #31507C;
  --scene-hoodie: #33415E;
  --scene-hoodie-hi: #455879;
  --scene-hair: #6E4A32;
  --scene-hair-hi: #8F6A47;
  --scene-skin: #E7B78C;
  --scene-lamp: #FFD98A;
  --scene-shine: rgba(255, 255, 255, .26);
  min-height: 560px;
}

/* D-013 rule 2: the voxel canvas is appended LAST inside .scene-frame, so the
   UI chips have to be lifted above it or they vanish under the render.
   The child combinator only reaches DIRECT children, so the bottom band has to
   be named too: .trip-train-route and .trip-train-votechip live one level down,
   inside .trip-train-bottombar, and were painted over by the canvas the moment
   it faded in (progress and vote chip both invisible, confirmed in capture on
   2026-08-18). Lifting the band lifts its whole subtree with it, which is why
   its children can stay position:static for the flex layout. */
.trip-train-scene > .scene-float,
.trip-train-scene > .trip-train-bottombar { z-index: 2; }

:root[data-theme="dark"] .trip-train-scene {
  --scene-sky-top: #493460;
  --scene-sky-mid: #6A4A6D;
  --scene-sky-bottom: #8C6A6C;
  --scene-sun: #F2C14E;
  --scene-fuji: #5B5A85;
  --scene-fuji-2: #454268;
  --scene-snow: #E8E4F5;
  --scene-hill-back: #3E4A63;
  --scene-hill-front: #324058;
  --scene-wood: #241B3B;
  --scene-wood-trim: #382A56;
  --scene-wood-light: #4A3A6B;
  --scene-bench-a: #5B6FB0;
  --scene-bench-b: #3D4A82;
  --scene-hoodie: #4A5A82;
  --scene-hoodie-hi: #5E7099;
  --scene-hair: #4A3626;
  --scene-hair-hi: #6B4E36;
  --scene-skin: #D1A17A;
  --scene-lamp: #F2C14E;
  --scene-shine: rgba(255, 255, 255, .13);
}

.trip-train-art {
  background: linear-gradient(180deg, var(--scene-wood-trim), var(--scene-wood));
}

/* ui-fluide layer 1 (D-022 dérogation on mount ORDERING only): the voxel
   canvas (engine.js: <canvas class="scene-canvas">) bakes synchronously
   (200-340ms, D-012/D-023) inside mountVoxel(); trip-train.js defers the
   mount itself (rAF + setTimeout 0) and only adds .is-baked to the frame
   once mountVoxel() has actually returned -- by then the canvas already
   holds real painted pixels (engine.js's mountVoxel ends with the
   synchronous `rebuild(); start();`). Until then this rule keeps the canvas
   invisible so the CSS fallback scene above (.trip-train-art, D-003) shows
   through as the masked-montage placeholder: it already uses the --scene-*
   tokens sanctioned by ARCHITECTURE.md rule 1, so unlike shop's token
   gradient it needs no separate placeholder value of its own. */
.trip-train-scene .scene-canvas {
  opacity: 0;
  transition: opacity .22s cubic-bezier(0, 0, 0.2, 1);
}
.trip-train-scene.is-baked .scene-canvas {
  opacity: 1;
}
/* D-031 : train.js appelle V.sky(...), le bake peint donc un ciel opaque sur
   tout le canvas (paintSky() dans engine.js remplit canvas.width x height
   entier), qui couvre deja completement .trip-train-art (meme boite,
   inset:0). Le decor CSS de secours (soleil, nuages, arbres, reflet vitre,
   lueur de lampe, vapeur du cafe, appareil photo floaty) ne se voit donc
   plus une fois baked, mais continuait de tourner en boucle invisible :
   display:none l'arrete net (contrairement a une simple opacite), sans
   toucher au rendu puisqu'il etait deja masque par le canvas opaque. */
.trip-train-scene.is-baked .trip-train-art {
  display: none;
}

/* ---------- window frame + glass (the view) ---------- */

.trip-train-window {
  position: absolute;
  top: 4%; left: 4%; right: 4%; height: 36%;
  border-radius: var(--r-xl) var(--r-xl) var(--r-md) var(--r-md);
  background: linear-gradient(175deg, var(--scene-wood-light), var(--scene-wood-trim));
  padding: 9px;
  box-shadow: var(--shadow-2), inset 0 0 0 2px color-mix(in srgb, var(--scene-wood) 55%, transparent);
}

.trip-train-glass {
  position: relative; width: 100%; height: 100%;
  border-radius: var(--r-lg) var(--r-lg) var(--r-sm) var(--r-sm);
  overflow: hidden;
  box-shadow: inset 0 10px 22px var(--shadow-warm-2);
}

.trip-train-sky {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, var(--scene-sky-top), var(--scene-sky-mid) 55%, var(--scene-sky-bottom));
}

.trip-train-sun {
  position: absolute; top: 8%; right: 14%;
  width: 20%; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--scene-sun) 0%, transparent 72%);
  opacity: .85;
  animation: trip-train-sun-breathe 8s ease-in-out infinite;
}
@keyframes trip-train-sun-breathe {
  0%, 100% { opacity: .72; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.09); }
}

.trip-train-cloud {
  position: absolute;
  height: 6%; border-radius: 999px;
  background: color-mix(in srgb, var(--scene-snow) 62%, transparent);
  animation: trip-train-cloud-drift 30s linear infinite;
}
.trip-train-cloud-1 { top: 12%; left: -22%; width: 18%; }
.trip-train-cloud-2 { top: 24%; left: -40%; width: 12%; height: 4.5%; animation-duration: 42s; animation-delay: -14s; }
@keyframes trip-train-cloud-drift {
  from { transform: translateX(0); }
  to { transform: translateX(760%); }
}

/* Mount Fuji : real width/height + clip-path, never percentage borders
   (percentage border-width is invalid CSS, which is why it used to render 0x0). */
.trip-train-fuji {
  position: absolute; bottom: 15%; left: 50%; transform: translateX(-50%);
  width: 42%; height: 38%;
  background: linear-gradient(200deg, var(--scene-fuji) 55%, var(--scene-fuji-2) 100%);
  clip-path: polygon(50% 0%, 8% 100%, 92% 100%);
  filter: var(--drop-1);
}
.trip-train-fuji-snow {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 62%; height: 48%;
  background: var(--scene-snow);
  opacity: .94;
  clip-path: polygon(50% 0%, 100% 100%, 82% 82%, 68% 100%, 50% 76%, 32% 100%, 18% 82%, 0% 100%);
}

.trip-train-hills {
  position: absolute; left: -10%; width: 120%; bottom: 0;
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.trip-train-hills-back { height: 20%; background: var(--scene-hill-back); opacity: .9; }
.trip-train-hills-front { height: 13%; left: -20%; width: 140%; background: var(--scene-hill-front); }

.trip-train-trees {
  position: absolute; left: 0; right: 0; bottom: 0; height: 12%;
  overflow: hidden;
}
.trip-train-trees-track {
  display: flex; width: 200%; height: 100%;
  animation: trip-train-trees-scroll 18s linear infinite;
}
.trip-train-trees-half {
  display: flex; align-items: flex-end; justify-content: space-between;
  width: 50%; padding: 0 2%;
}
.trip-train-trees-half span {
  font-size: clamp(12px, 2vw, 18px);
  opacity: .85;
  filter: var(--drop-1);
}
@keyframes trip-train-trees-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.trip-train-glass-shine {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 34%, var(--scene-shine) 48%, transparent 62%);
  background-size: 240% 240%;
  background-position: -50% -50%;
  animation: trip-train-shine-sweep 9s ease-in-out infinite;
}
@keyframes trip-train-shine-sweep {
  0%, 12% { background-position: -60% -60%; opacity: .5; }
  50% { background-position: 40% 40%; opacity: .95; }
  88%, 100% { background-position: 140% 140%; opacity: .4; }
}

/* ---------- sill ---------- */

.trip-train-sill {
  position: absolute; left: 4%; right: 4%; top: 40%; height: 4%;
  background: linear-gradient(180deg, var(--scene-wood-light), var(--scene-wood-trim));
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-1);
}

/* ---------- interior (warm cabin, lower half) ---------- */

.trip-train-interior {
  position: absolute; left: 4%; right: 4%; top: 44%; height: 20%;
}

.trip-train-lamp-glow {
  position: absolute; right: 4%; top: -34%;
  width: 22%; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--scene-lamp) 0%, transparent 70%);
  opacity: .5; pointer-events: none;
  animation: trip-train-lamp-breathe 5s ease-in-out infinite;
}
@keyframes trip-train-lamp-breathe {
  0%, 100% { opacity: .38; }
  50% { opacity: .68; }
}

.trip-train-bench {
  position: absolute; left: 0; right: 40%; bottom: 0; height: 78%;
  background: linear-gradient(165deg, var(--scene-bench-a), var(--scene-bench-b));
  border-radius: var(--r-lg) var(--r-lg) var(--r-sm) var(--r-sm);
  box-shadow: var(--shadow-1);
}

.trip-train-traveler {
  position: absolute; left: 2%; bottom: 0; width: 33%; height: 97%;
}
.trip-train-traveler-body {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 94%; height: 70%;
  background: linear-gradient(165deg, var(--scene-hoodie-hi), var(--scene-hoodie));
  border-radius: 42% 42% 12% 12% / 36% 36% 8% 8%;
  box-shadow: var(--shadow-1);
}
/* jacket collar: wider than the head, so it still peeks past the head's
   sides : reads as shoulders wearing a collar, not a disc floating loose. */
.trip-train-traveler-collar {
  position: absolute; left: 50%; bottom: 61%; transform: translateX(-50%);
  width: 56%; height: 15%;
  background: linear-gradient(180deg, var(--scene-hoodie-hi), var(--scene-hoodie));
  border-radius: 50% 50% 18% 18% / 70% 70% 18% 18%;
  box-shadow: var(--shadow-1);
}
/* nape sliver: mostly covered by hair, visible at the sides beneath it, 
   the "seen from behind, seated" cue the flat disc used to erase. */
.trip-train-traveler-neck {
  position: absolute; left: 50%; bottom: 63%; transform: translateX(-50%);
  width: 32%; height: 13%;
  background: var(--scene-skin);
  border-radius: 40% 40% 0 0;
}
/* Head sized off the traveler's HEIGHT, not its width, the old bug set
   width:48% of a wide-but-short wrapper and let aspect-ratio square that
   into a 101px disc as tall as the whole body. height:37% is a credible
   head/body ratio, and aspect-ratio derives a matching width from it, so
   it can no longer balloon just because the wrapper is wide. */
.trip-train-traveler-head {
  position: absolute; left: 50%; bottom: 60%; transform: translateX(-50%);
  width: auto; height: 37%; aspect-ratio: 1;
  background: linear-gradient(165deg, var(--scene-hair-hi), var(--scene-hair));
  border-radius: 46% 46% 40% 40% / 58% 58% 32% 32%;
  box-shadow: var(--shadow-1);
}

.trip-train-table {
  position: absolute; left: 64%; right: 2%; bottom: 0; height: 60%;
  background: linear-gradient(180deg, var(--scene-wood-trim), var(--scene-wood));
  border-radius: var(--r-md) var(--r-md) var(--r-sm) var(--r-sm);
  box-shadow: var(--shadow-1);
  display: flex; align-items: center; justify-content: center;
  gap: clamp(8px, 2.4vw, 18px);
  padding: 0 8px;
}

.trip-train-item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-size: clamp(17px, 2.3vw, 25px);
}
.trip-train-item small {
  font-size: 7.5px; font-weight: 900; letter-spacing: .03em;
  color: var(--ink); background: var(--card);
  padding: 2px 5px; border-radius: 6px; white-space: nowrap;
  box-shadow: var(--shadow-1);
}
.trip-train-steam {
  position: absolute; top: -6px; left: 55%;
  font-size: 10px; color: var(--card);
}

/* ---------- floating overlays: focus + pass ---------- */

.trip-train-float-focus {
  top: 14px; left: 14px;
  max-width: min(190px, 46%);
}
.trip-train-timer { font-size: 21px; color: var(--ink); }
/* was an inline style="color:var(--accent)" on a shared component, the only
   kicker in the app carrying its colour inline. Now a class, like everywhere. */
.trip-train-focus-kicker { color: var(--accent); }
.trip-train-focus-hint { font-size: 10.5px; font-weight: 700; margin-top: 2px; }
.trip-train-live-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--muted);
  display: inline-block;
}
.trip-train-float-focus[data-status="running"] .trip-train-live-dot {
  background: var(--accent);
  animation: trip-train-pulse 1.6s ease-in-out infinite;
}
.trip-train-float-focus[data-status="paused"] .trip-train-live-dot { background: var(--gold); }
@keyframes trip-train-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .4; transform: scale(.7); }
}

.trip-train-float-pass {
  top: 14px; right: 14px;
  max-width: min(220px, 48%);
}
.trip-train-pass-route {
  margin-top: 8px; font-size: 13.5px; font-weight: 900;
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
}
.trip-train-pass-arrow { color: var(--muted); font-weight: 700; }
.trip-train-pass-meta { font-size: 10.5px; font-weight: 800; margin-top: 4px; color: var(--muted); letter-spacing: .02em; }
.trip-train-barcode {
  width: 30px; height: 26px; border-radius: 3px; opacity: .85; flex: none;
  background: repeating-linear-gradient(90deg,
    var(--ink) 0 2px, transparent 2px 4px,
    var(--ink) 4px 5px, transparent 5px 8px,
    var(--ink) 8px 11px, transparent 11px 13px,
    var(--ink) 13px 14px, transparent 14px 17px,
    var(--ink) 17px 19px, transparent 19px 21px,
    var(--ink) 21px 22px, transparent 22px 25px,
    var(--ink) 25px 27px, transparent 27px 30px);
}

/* ---------- footer band: route + vote chip, own strip, never over the seat ---------- */

.trip-train-bottombar {
  position: absolute; left: 14px; right: 14px; bottom: 14px;
  display: flex; flex-wrap: wrap; align-items: stretch; gap: 12px;
}
.trip-train-bottombar .scene-float { position: static; }

.trip-train-route {
  flex: 1 1 280px; min-width: 0;
  padding: 12px 16px;
}
.trip-train-route-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center;
}
.trip-train-route-pt {
  flex: 0 0 auto;
  font-size: 11.5px; font-weight: 800; color: var(--ink-soft);
}
.trip-train-route-pt b { color: var(--ink); }
.trip-train-route-track {
  position: relative; flex: 1 1 80px; min-width: 60px; height: 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 70%, transparent);
}
.trip-train-route-fill {
  position: absolute; inset: 0; width: 35%;
  border-radius: 999px; background: var(--warm);
  transition: width .5s ease;
}
.trip-train-route-train {
  position: absolute; top: 50%; left: 35%;
  transform: translate(-50%, -50%);
  font-size: 15px;
  transition: left .5s ease;
  filter: var(--drop-2);
}
.trip-train-route-remain {
  margin-top: 6px; text-align: center;
  font-size: 11px; color: var(--warm); letter-spacing: .02em;
}
.trip-train-route-pt b { letter-spacing: .02em; }

.trip-train-votechip {
  flex: 0 1 220px; min-width: 0;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px;
}
.trip-train-votering {
  flex: none;
  width: 42px; height: 42px; border-radius: 50%;
  background: conic-gradient(var(--warm) 0 var(--jp-pct, 54%), var(--blue) 0 100%);
  /* only the alpha channel of this stop matters to a mask; var(--ink) is
     opaque in both themes, so it replaces the hard-coded #000 without any
     visual change (ARCHITECTURE.md rule 1: tokens only, no hex in dur). */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 7px), var(--ink) calc(100% - 7px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 7px), var(--ink) calc(100% - 7px));
  box-shadow: var(--shadow-1);
}
.trip-train-vote-info { min-width: 0; }
.trip-train-vote-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px;
}
.trip-train-vote-split { font-size: 10.5px; font-weight: 800; margin-top: 2px; }

/* ---------- cards below scene ---------- */

.trip-train-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
}

.trip-train-culture, .trip-train-playlist, .trip-train-session { padding: 20px; }

.trip-train-culture-text {
  margin-top: 12px;
  font-size: 13.5px; color: var(--ink-soft); font-weight: 600; line-height: 1.5;
  min-height: 3.2em;
  transition: opacity .22s ease, transform .22s ease;
}
.trip-train-culture-text.is-fading { opacity: 0; transform: translateY(4px); }
.trip-train-vibe { font-size: 18px; }

.trip-train-thumbs {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.trip-train-thumb {
  aspect-ratio: 1; border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}
.trip-train-thumb-1 { background: linear-gradient(135deg, var(--violet-soft), var(--blue-soft)); }
.trip-train-thumb-2 { background: linear-gradient(135deg, var(--blue-soft), var(--gold-soft)); }
.trip-train-thumb-3 { background: linear-gradient(135deg, var(--warm-soft), var(--violet-soft)); }
.trip-train-thumb-4 { background: linear-gradient(135deg, var(--gold-soft), var(--warm-soft)); }
.trip-train-thumbs.is-playing .trip-train-thumb { animation: trip-train-thumb-pulse 1.8s ease-in-out infinite; }
.trip-train-thumbs.is-playing .trip-train-thumb-2 { animation-delay: .15s; }
.trip-train-thumbs.is-playing .trip-train-thumb-3 { animation-delay: .3s; }
.trip-train-thumbs.is-playing .trip-train-thumb-4 { animation-delay: .45s; }
@keyframes trip-train-thumb-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(.92); }
}

/* ---------- session controls ---------- */

.trip-train-session-row { flex-wrap: wrap; gap: 12px; }
.trip-train-session-sub { font-size: 12.5px; font-weight: 700; margin-top: 1px; }
.trip-train-session-ico {
  width: 38px; height: 38px; border-radius: var(--r-sm); font-size: 18px;
  display: grid; place-items: center;
  background: var(--bg-2);
}

/* ---------- responsive ---------- */
/* Mobile-first: window/sill/interior percentages above are the narrow-viewport
   split (short window, tall reserved footer so a 2-row route+vote footer never
   climbs into the seat). Widen the window and shrink the footer once there is
   enough horizontal room for the footer to stay on one row. */

@media (min-width: 760px) {
  .trip-train-scene { min-height: 460px; }
  .trip-train-window { height: 44%; }
  .trip-train-sill { top: 48%; height: 4%; }
  .trip-train-interior { top: 52%; height: 26%; }
}

@media (min-width: 1000px) {
  .trip-train-scene { min-height: 480px; }
}

@media (max-width: 640px) {
  .trip-train-float-focus, .trip-train-float-pass { padding: 8px 10px; }
  .trip-train-timer { font-size: 17px; }
  .trip-train-pass-route { font-size: 12px; }
  .trip-train-route-pt { font-size: 10px; }
  .trip-train-route-remain { font-size: 10px; }
  .trip-train-votechip { padding: 8px 12px; }
}

@media (max-width: 420px) {
  .trip-train-float-focus, .trip-train-float-pass { max-width: 47%; }
  .trip-train-focus-hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .trip-train-sun,
  .trip-train-cloud,
  .trip-train-trees-track,
  .trip-train-glass-shine,
  .trip-train-lamp-glow,
  .trip-train-thumbs.is-playing .trip-train-thumb,
  .trip-train-steam,
  .trip-train-camera {
    animation: none;
  }
  /* was `.trip-train-live-dot` (specificity 0,1,0): weaker than the rule that
     actually applies the infinite pulse, `.trip-train-float-focus[data-status
     ="running"] .trip-train-live-dot` (0,3,0, two classes + one attribute).
     A weaker selector can never override a stronger one regardless of source
     order, so the pulse kept looping under reduced-motion during an active
     session (the global net in base.css clamps animation-duration but not
     animation-iteration-count): a stroboscope, exactly the piège the doctrine
     warns about. Matching the activation selector's specificity fixes it. */
  .trip-train-float-focus[data-status="running"] .trip-train-live-dot {
    animation: none;
  }
  /* ui-fluide layer 1: the global net (base.css) clamps transition-duration
     to .01ms already, but never touches a residual transition-delay; this
     screen sets none explicitly on its one JS-gated reveal, so appearance is
     immediate the instant .is-baked lands, no fade, no residual delay left
     to the net (same reasoning as shop.css's identical rule). */
  .trip-train-scene .scene-canvas {
    transition: none;
  }
}
