/* Journey — slice 9 of 9 of the former components.css.

   ⚠️ LOAD ORDER IS PART OF THESE FILES. They are CONTIGUOUS slices of one
   6841-line sheet, cut at section banners and never re-sorted, so concatenating
   them in the order index.html lists them reproduces the original byte for byte
   — which is the proof the cascade did not shift. Moving a rule to a different
   slice throws that guarantee away. index.html and sw.js's SHELL_ASSETS must
   list all 9 in the same order as each other.

   Contents: Journey map, day stones, panel navigation, page shell. */

/* ── 👣 My Journey Log (decision #125) ─────────────────────────────────────
   The calendar page's map. Sizes come from JS (journey.js solves the hex from
   the measured container and writes cell size AND pitch from the same number),
   so nothing here may set a hex width — that is what keeps rendered size and
   layout pitch equal by construction. */
.wu-jrn {
  position: relative;
  /* FULL PAGE (#125d). It fills whatever its host gives it — on the journey
     page that is the whole layer below the header. No viewport maths here: the
     host owns the height, and the row solver reads the result. */
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.wu-jrn-head {
  /* FLOATS OVER the map, like a map control, rather than taking a row above it.
     In flow it read as a third level of chrome under the page header, and it
     spent height the full-page map wants. Out of flow, the scroller measures
     the whole box — which is also what the row solver reads. */
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.wu-jrn-today-btn {
  flex: none;
  /* There is no global `* { box-sizing }` in this project (#90), so anything
     with a border AND a stretched width must opt in or it overflows by the
     border. That is what put a 3px sideways scroll on the calendar page. */
  box-sizing: border-box;
  min-height: 36px;
  padding: 0 var(--space-md);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-pill);
  background: var(--colour-surface);
  color: var(--colour-text-secondary);
  font: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
  position: relative;
}
/* 44px hit area under a 36px pill — the floor from #88-#90, bought with a
   transparent ::after so the pill itself stays visually light. */
.wu-jrn-today-btn::after {
  content: '';
  position: absolute;
  inset: -4px;
}
.wu-jrn-today-btn:hover {
  border-color: var(--colour-accent);
  color: var(--colour-accent);
}

.wu-jrn-scroll {
  flex: 1;
  min-height: 0;
  box-sizing: border-box;
  overflow-x: auto;
  overflow-y: hidden;
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-card);
  background: var(--colour-grid-bg);
  /* The map is the one place in this app that scrolls sideways ON PURPOSE. */
  overscroll-behavior-x: contain;
}
.wu-jrn-track {
  position: relative;
}

.wu-jrn-lattice {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Same two-layer construction as .wu-ghost-hex: a 1px ring of grid-line with
   the pane colour inset on top, so the cell fill and the gap read as one
   padding zone. Kept faint here — terrain (slice 2) is what makes it prominent. */
.wu-jrn-ghost {
  position: absolute;
}
.wu-jrn-ghost::before,
.wu-jrn-ghost::after {
  content: '';
  position: absolute;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}
.wu-jrn-ghost::before {
  inset: 0;
  background: var(--colour-grid-line);
}
/* TERRAIN (#125e). The cell's fill is the ground; journey.js sets --t-col and
   --t-a from the nearest day that actually had completions. Defaults paint
   nothing, so an untouched stretch is plain ground and no rule has to special-
   case it — which is what makes "renders what WAS done" true by construction. */
.wu-jrn-ghost::after {
  inset: 1px;
  background: color-mix(
    in srgb,
    var(--t-col, transparent) calc(var(--t-a, 0) * 100%),
    var(--colour-grid-bg)
  );
}

/* Planted from history, decorative by design: the day stone underneath is the
   tap target, so a landmark must never intercept it. */
.wu-jrn-lm {
  position: absolute;
  pointer-events: none;
  z-index: 1;
  line-height: 1;
  filter: drop-shadow(0 1px 1px rgb(43 41 38 / 0.18));
}

.wu-jrn-road {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.wu-jrn-road polyline {
  stroke: color-mix(in srgb, var(--colour-brand) 42%, var(--colour-border));
  opacity: 0.75;
}

/* Sequence dividers. With the empty days collapsed these carry the time axis,
   so they are a full-height rule rather than a floating caption — two stones
   either side of one may be a day or a month apart. */
.wu-jrn-div {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
  border-left: 1px dashed var(--colour-border);
  pointer-events: none;
  /* The chip hangs at the BOTTOM of the full-height hairline, so the run of week
     markers reads as one ruler along the axis you scroll. */
  display: flex;
  align-items: flex-end;
}
.wu-jrn-divlab {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin: 0 0 7px 6px;
  /* 5px, not 8: the chip holds a bare week number now, and MEASURED against real
     multi-month data the tightest divider gap is 44px while an 8px-padded chip is
     46px — it overlapped its neighbour by 2px at 375px wide. At 5px it is ~40px
     and clears. The number is the content; the padding was sized for a chip that
     also carried a month. */
  padding: 3px 5px;
  white-space: nowrap;
  border-radius: var(--radius-pill, 999px);
  /* A chip, not bare text: it has to stay legible where it crosses terrain. */
  background: color-mix(in srgb, var(--colour-surface) 88%, transparent);
  border: 1px solid var(--colour-border);
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  line-height: 1;
}
.wu-jrn-div b {
  font-size: var(--font-size-xs, 11px);
  color: var(--colour-text-secondary);
  font-variant-numeric: tabular-nums;
}
/* The pill is the WEEK's tap target (#125d). It sits in the 30px axis band, so
   the hit area is grown downward and outward with a pseudo-element rather than
   by making the chip itself 44px tall, which would swallow the band. */
.wu-jrn-divlab {
  position: relative;
  border: 1px solid var(--colour-border);
  cursor: pointer;
  pointer-events: auto;
}
.wu-jrn-divlab::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: max(44px, 100%);
  height: 44px;
  transform: translate(-50%, -50%);
}
.wu-jrn-divlab:hover {
  border-color: var(--colour-accent);
}
.wu-jrn-divlab-on {
  border-color: var(--colour-accent);
  background: color-mix(in srgb, var(--colour-accent) 16%, var(--colour-surface));
}
.wu-jrn-divlab-on b {
  color: var(--colour-accent);
}
/* THE MONTH LINE — its own element, not a flag on the week divider, because a
   month boundary lands wherever it lands and most months start MID-WEEK. Solid
   and accent-tinted against the weekly dashed run: the coarse anchor reading
   over the top of the fine one. */
.wu-jrn-mdiv {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
  border-left: 1px solid color-mix(in srgb, var(--colour-accent) 40%, transparent);
  pointer-events: none;
  /* BOTH SCALES AT THE FOOT (owner, 2026-08-31), the month sitting directly above
     the week chips. Splitting them to opposite ends of the line made each one
     legible on its own but put the coarse anchor at the far end of the screen
     from the fine one — so reading "which week of which month" meant looking in
     two places. Stacked, the ruler is one block you read in a single glance, and
     it is what makes the month on every chip redundant. */
  display: flex;
  align-items: flex-end;
}

/* THE MONTH ANNOUNCEMENT (owner, 2026-08-30). Set in the display face, tracked
   out and in the accent — the map's only piece of typography that is meant to be
   read from across the room, because it is the one label that says WHERE IN THE
   YEAR you are. Deliberately NOT a chip like the week pill: a second pill would
   read as another tappable thing at the other end of the same line, and this is
   a caption for a region rather than a control. January carries its year, which
   is the only boundary where the month name alone is ambiguous. */
.wu-jrn-month {
  /* Clears the week chip below it: 7px of chip margin + ~22px of chip, then a
     small gap. AXIS_H in journey.js reserves the band for both rows. */
  margin: 0 0 33px 8px;
  padding: 0;
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-sm);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--colour-accent) 78%, var(--colour-text-primary));
  /* It sits over terrain that can be any activity colour, so it carries its own
     ground the way the week chip does — but as a halo rather than a box, which
     keeps it a caption. */
  text-shadow:
    0 0 3px var(--colour-surface), 0 0 3px var(--colour-surface),
    0 0 6px var(--colour-surface);
}

/* ── Day stones ─────────────────────────────────────────────────────────── */
.wu-jrn-stone {
  position: absolute;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  color: var(--colour-text-primary);
  cursor: pointer;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 2px;
  line-height: 1;
}
/* box-shadow cannot be used on a clip-path element — it is clipped away. The
   ring is a second clipped layer instead, exactly as the ghost lattice does. */
.wu-jrn-stone::before,
.wu-jrn-stone::after {
  content: '';
  position: absolute;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}
.wu-jrn-stone::before {
  inset: 0;
  background: var(--stone, var(--colour-text-secondary));
  opacity: 0.55;
}
.wu-jrn-stone::after {
  inset: 2px;
  background: color-mix(
    in srgb,
    var(--stone, var(--colour-border)) calc(var(--fill, 0) * 55%),
    var(--colour-surface)
  );
}
.wu-jrn-stone > * {
  position: relative;
  z-index: 1;
}
.wu-jrn-stone:hover::before {
  opacity: 1;
}

/* Ahead of today is MISTY, not locked — the days are coming whatever happens,
   and a padlock would say the app is withholding them. */
.wu-jrn-future {
  opacity: 0.5;
}
.wu-jrn-future::before {
  opacity: 0.3;
}
.wu-jrn-today::before {
  background: var(--colour-accent);
  opacity: 1;
  /* A slow breath, not a blink: this runs for as long as the map is open, so it
     has to be ignorable. Scaling the RING layer (which sits behind the fill at
     inset 2px) grows a halo outside the stone without touching the fill or the
     text, and transform is composited — no layout, no repaint of the lattice. */
  animation: wu-jrn-beat 2.6s ease-in-out infinite;
}
@keyframes wu-jrn-beat {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
  .wu-jrn-today::before { animation: none; }
}
.wu-jrn-sel::before {
  background: var(--colour-accent);
  opacity: 1;
}
.wu-jrn-sel::after {
  inset: 3px;
}
/* Every stone of a selected WEEK, so stepping the panel walks the map. Lighter
   than a day selection: it marks a range, it is not a single chosen thing. */
.wu-jrn-selweek::before {
  outline: 2px solid color-mix(in srgb, var(--colour-accent) 55%, transparent);
  outline-offset: -2px;
}

/* ── Journey panel navigation ──────────────────────────────────────────────
   Prev / next stepping WITHIN the open scope — day by day, or week by week. */
.wu-jrn-nav {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex: none;
}
.wu-jrn-navlab {
  flex: 1;
  min-width: 0;
  /* min-width: 0 lets the label YIELD to the two 44px chevrons; without a
     truncation rule it would simply spill over them instead. A cross-month span
     ("W40 · 28 Sep – 4 Oct") is the widest this gets. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  color: var(--colour-text-primary);
}
.wu-jrn-navbtn {
  flex: none;
  width: 44px;
  height: 44px;
  border: 1px solid var(--colour-border);
  border-radius: 50%;
  background: var(--colour-surface);
  color: var(--colour-text-primary);
  font-size: var(--font-size-lg);
  line-height: 1;
  cursor: pointer;
}
.wu-jrn-navbtn:hover:not(:disabled) {
  border-color: var(--colour-accent);
  color: var(--colour-accent);
}
.wu-jrn-navbtn:disabled {
  opacity: 0.35;
  cursor: default;
}

.wu-jrn-dow {
  font-size: var(--fs-dow, 11px);
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--colour-text-secondary);
  text-transform: uppercase;
}
.wu-jrn-num {
  font-size: var(--fs-num, 17px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.wu-jrn-pips {
  display: flex;
  gap: 2px;
  align-items: center;
}
.wu-jrn-pips i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
}
.wu-jrn-pips b {
  font-size: 8px;
  color: var(--colour-text-secondary);
}
.wu-jrn-mark {
  font-size: 0.72em;
}

/* Rides the map on today's stone — the one thing that says the map is YOURS
   rather than a chart of you. */
.wu-jrn-avatar {
  position: absolute;
  transform: translateX(-50%);
  display: grid;
  place-items: center;
  z-index: 2;
  pointer-events: none;
  box-shadow: 0 2px 6px rgb(43 41 38 / 0.28);
}


/* ── Journey page shell: map full-bleed, detail as a sliding side panel ──── */
/* FULL BLEED (owner, 2026-08-30: "is there a need for the white padding? just
   max the available space for the journey"). The layer's --space-lg padding and
   its header row were both reserving room for chrome that has now MOVED to the
   rail — a header bar, a centred title and two buttons. Only the camera inset
   survives, because that intrusion is physical rather than drawn (§6). */
.wu-drill-layer.wu-jrnpage-full {
  padding: 0;
  padding-top: var(--safe-top);
}
/* Edge to edge inside the pane's OWN rounded corner. A card border and radius
   here would draw a second frame a few pixels inside the shell's, which is
   exactly the "not parallel" margin §6 describes. */
.wu-jrnpage-full .wu-jrn-scroll {
  border: 0;
  border-radius: 0;
}

/* The journey's ✕ · 📋 Log · 📍 Today were a private copy of the rail's stack
   for one day (2026-09-26); they are now the SHARED .wu-modal-chrome column
   (#164), which every full-screen modal gets, so there is nothing here. The map
   stays full-bleed and the column floats over it — reserving one would undo
   what #129 bought, which is why .wu-jrnpage-full zeroes the layer's padding. */

.wu-jrnpage {
  position: relative;
  flex: 1;
  min-height: 0;
  width: 100%;
  overflow: hidden;
}
.wu-jrnpage-map {
  height: 100%;
  min-height: 0;
}
.wu-jrnpage-list-btn {
  flex: none;
  box-sizing: border-box;
  min-height: 36px;
  padding: 0 var(--space-md);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius-pill);
  background: var(--colour-surface);
  color: var(--colour-text-secondary);
  font: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
  position: relative;
}
.wu-jrnpage-list-btn::after {
  content: '';
  position: absolute;
  inset: -4px;
}
.wu-jrnpage-list-btn[aria-expanded="true"] {
  border-color: var(--colour-accent);
  color: var(--colour-accent);
}

/* Slides from the RAIL SIDE, never centred — a centred modal covers the stone
   you just tapped, which is the context you selected it for. */
.wu-jrnpage-panel {
  position: absolute;
  inset: 0 auto 0 0;
  z-index: 3;
  box-sizing: border-box;
  width: min(400px, 78%);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--colour-surface);
  border-right: 1px solid var(--colour-border);
  box-shadow: 6px 0 20px rgb(43 41 38 / 0.12);
  transform: translateX(-101%);
  transition: transform 200ms ease;
  overflow-y: auto;
}
.wu-jrnpage-panel-open {
  transform: translateX(0);
}
@media (prefers-reduced-motion: reduce) {
  .wu-jrnpage-panel {
    transition: none;
  }
}
/* THE PANEL'S TOP BAR (batch item 5, 2026-09-26). The ✕ had a 44px row to
   itself with ~315 of 343px empty, and the scope label sat two rows under it —
   60px of chrome (the row, the column gap and the head's own margin) spent on a
   line of text. They share a row now, so the log starts 60px higher.

   ORDER, not align-self: in a COLUMN `align-self` chose the ✕'s side, and in a
   ROW that property means vertical alignment instead. The ✕ keeps the side it
   had — trailing beside the map, leading on a portrait phone, where it is the
   only way out and the top-right band is the one an Android status cluster owns
   (#108, §6). */
.wu-jrn-panelbar {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-sm);
}
.wu-jrn-panelbar .wu-calpage-head {
  flex: 1;
  min-width: 0;
  /* Its own bottom margin was the gap to the list; the bar's row supplies it. */
  margin-bottom: 0;
}

.wu-jrnpage-panel-close {
  order: 2;
  flex: none;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--colour-text-secondary);
  font-size: var(--font-size-lg);
  cursor: pointer;
}
.wu-jrnpage-panel-close:hover {
  background: var(--colour-grid-bg);
  color: var(--colour-text-primary);
}
.wu-jrnpage-panel .wu-set-list {
  flex: 1;
  min-height: 0;
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
}

/* On a PORTRAIT PHONE the panel is FULL SCREEN, not a bottom sheet (owner,
   2026-09-19: "the journey is hidden behind the log — we either recenter the
   journey on the available window above or we make the log full screen on
   mobile?"). A 62% sheet left the map 38% of a phone, and THE MAP CANNOT SCROLL
   VERTICALLY to compensate — it scrolls along the road — so the lower rows simply
   went behind the sheet with no way to reach them.
   Recentring was the other option and buys a strip rather than a map: at 812px
   tall the space left is ~250px once the ruler's 52px is out, three rows at best,
   and it would have to RE-SOLVE the row count on open and close, reflowing the
   whole map so the stone the child just tapped MOVES under the panel opening
   over it. Full screen gives the list the whole pane, and the map is one ✕ away.
   The panel stands alone because #125 already made it: it names its own day or
   week and steps with ‹ ›, and it carries its own close control. It covers
   the rail as well as the map (2026-09-25 — see the block below). Landscape
   keeps the side panel (height is the scarce axis there, which is exactly what
   a side panel spends nothing of). */
@media (max-width: 900px) and (orientation: portrait) {
  /* FULL SCREEN MEANS ABOVE THE RAIL TOO (owner, 2026-09-25: "can we apply the
     same 'modal' approach for the journey so that when a day is tapped it
     becomes a full screen pop up. Works well as a side bar on larger screens").
     This reverses the one line of #152(a) that kept the rail on top. It was the
     careful call at the time — ★ Home and ✕ never go — but living with it, the
     panel is a MODAL: it is opened to be read and dismissed, it carries its own
     ✕, and holding 56px of spine beside it buys a second way out that costs the
     list a sixth of a phone's width. The calendar modal took the same turn in
     #162, and two overlays with the same job must not disagree about whether
     the rail is part of them.
     `position: fixed` because the rail is fixed: an absolutely positioned panel
     can only ever fill its own pane, which starts to the right of the spine. */
  .wu-jrnpage-panel {
    position: fixed;
    inset: 0;
    z-index: 71; /* the calendar modal's layer — above the rail's 60 */
    width: auto;
    max-height: none;
    border-right: 0;
    box-shadow: none;
    transform: translateY(101%);
    /* Fixed, so body's safe-area padding no longer reaches it and it has to
       carry its own: --support-top clears the camera AND an Android status
       cluster, the other three the gesture bar and the curved edges. */
    padding: var(--support-top)
             calc(var(--space-md) + var(--safe-right))
             calc(var(--space-md) + var(--safe-bottom))
             calc(var(--space-md) + var(--safe-left));
  }
  .wu-jrnpage-panel-open {
    transform: translateY(0);
  }
  /* AND THE LAYER HAS TO COME UP WITH IT. .wu-drill-layer is
     `position: absolute; z-index: 2`, which is a STACKING CONTEXT, so the
     panel's 71 is counted inside that 2 and the rail's 60 is still painted over
     it - measured, the element at the star's own position was still the star
     and the panel's first 56px sat behind the spine. A z-index means nothing
     until you know which context it is counted in. Lifted only while the panel
     is OPEN: the panel covers the whole viewport, so no part of the map is ever
     shown above the rail, and dropping the class puts the layer back under the
     rail on the first frame of the slide out. */
  .wu-drill-layer:has(.wu-jrnpage-panel-open) {
    z-index: 71;
  }
  /* The ✕ moves to the RAIL SIDE. It is now the only way out, and the top-right
     band is the one region an Android status cluster owns — the bug reported
     twice in 2026-07 and the reason #108 states the rule (§6). */
  .wu-jrnpage-panel-close {
    order: 0;
  }
}
