/* ============================================================================
   Cradle portal — layout + card system.

   Tokens come from the site's own styles.css, which transcribes
   ~/cradle/src/lib/theme.ts. Nothing here invents a colour. Two rules from the
   site's accessibility audit are load-bearing and repeated so they cannot be
   lost:

     1. --plum-mute (#9389B5) IS NEVER TEXT. It measures 2.89:1 on glass.
        Gridlines, dots and rules only. The app reached the same conclusion for
        its axis ticks and raised them to plumSoft.
     2. Text sitting directly on the gradient uses full --plum. --plum-soft is
        only safe on glass, and .sky is position:fixed so any element can end up
        over the darkest stop depending on scroll — assume the worst ground.

   Category colours survive the dusk restyle: sleep-blue, feed-coral and
   diaper-gold stay distinguishable. Dusk restyles SURFACES, not codes.
   ========================================================================= */

/* ------------------------------------------------------------------ shell */
.pshell { max-width: 1180px; margin: 0 auto; padding: 0 20px 80px; }

.ptop {
  position: sticky; top: 0; z-index: 40;
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  background: rgba(255,255,255,0.72);
  border-bottom: 1px solid var(--glass-rim);
}
.ptop-in {
  max-width: 1180px; margin: 0 auto; padding: 10px 20px;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
/* Both header controls are links, so both have to be reachable targets. The
   brand measured 87x28 and "Back to the site" 82x13 — a 13px-tall tap target
   in the top-right corner of every portal page, which the layout lint catches
   as `tiny-target` and which is miserable on a phone regardless. The text
   stays the size it is; the BOX grows around it. */
.pbrand { display: inline-flex; align-items: center; gap: 9px; min-height: 44px;
          font-weight: 800; letter-spacing: -0.02em; }
.ptop-actions > a, .ptop-actions > button {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px;
}
.pbrand .mark { width: 28px; height: 28px; flex: none; }

.ptabs { display: flex; gap: 4px; margin-left: auto; flex-wrap: wrap; }
.ptab {
  /* 38px minimum: the audit found the dashboard's only controls were 26px tall,
     under any reasonable thumb. */
  min-height: 38px; display: inline-flex; align-items: center;
  padding: 0 14px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 650; color: var(--plum-soft);
  border: 1px solid transparent; background: transparent;
}
.ptab:hover { background: rgba(255,255,255,0.6); }
/* Every control needs a visible focus state, not just a hover one: a keyboard
   user otherwise tabs through the whole navigation blind. The shell pages had
   :hover and nothing else. */
.ptab:focus-visible { outline: 3px solid var(--dusk-accent, #7563C2); outline-offset: 2px; }
.ptab[aria-current="page"] {
  background: #fff; color: var(--accent-ink);
  border-color: var(--glass-rim); box-shadow: var(--shadow-card);
}

/* --------------------------------------------------------- sample banner */
.pbanner {
  margin: 16px 0 0; padding: 12px 16px; border-radius: var(--r-md);
  background: rgba(255,255,255,0.78); border: 1px solid var(--glass-rim);
  border-left: 4px solid var(--warning); border-radius: 0 var(--r-md) var(--r-md) 0;
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 13.5px; color: var(--plum);
}
.pbanner b { color: var(--accent-ink); }

/* ------------------------------------------------------------------ grid */
.pgrid { display: grid; gap: 16px; margin-top: 18px; grid-template-columns: repeat(12, minmax(0, 1fr)); }
.c4  { grid-column: span 4; }
.c6  { grid-column: span 6; }
.c8  { grid-column: span 8; }
.c12 { grid-column: span 12; }

.psec {
  grid-column: 1 / -1; margin: 16px 0 -4px;
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--accent-ink);
}

/* ------------------------------------------------------------------ card
   The web equivalent of the app's DuskCard. */
.card2 {
  background: var(--glass-strong);
  border: 1px solid var(--glass-rim);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: 18px 18px 16px;
  /* minmax(0,1fr) above plus this: a 1fr grid track floors at min-content, and
     monospace/number runs have no break opportunity, which is how the old
     dashboard overflowed to 394px on a 375px screen and was silently CLIPPED
     (body is overflow-x: clip, so it could not even be scrolled to). */
  min-width: 0; overflow-wrap: anywhere;
}
.card2 > h3 {
  font-size: 15px; font-weight: 750; letter-spacing: -0.015em;
  color: var(--plum); margin-bottom: 2px;
}
.card2 .sub { font-size: 12.5px; color: var(--plum-soft); line-height: 1.45; }
.card2 .why {
  margin-top: 10px; font-size: 12px; color: var(--plum-soft);
  line-height: 1.5; padding-top: 10px; border-top: 1px solid rgba(147,137,181,0.28);
}

/* a value nobody can trace is worse than no value */
.prov { font-size: 11px; color: var(--plum-soft); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- big number */
.bignum {
  font-size: 40px; font-weight: 800; letter-spacing: -0.035em;
  color: var(--plum); font-variant-numeric: tabular-nums lining-nums;
  line-height: 1;
}
.bigunit { font-size: 15px; font-weight: 650; color: var(--plum-soft); letter-spacing: 0; }

/* ------------------------------------------------------------------ pills */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: var(--r-pill);
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.01em;
}
.pill-ok   { background: rgba(62,156,122,0.14);  color: var(--cta-ink); }
.pill-warn { background: rgba(224,163,61,0.18);  color: #7A5410; }
.pill-flat { background: rgba(117,99,194,0.13);  color: var(--accent-ink); }

/* ------------------------------------------------------- range + controls */
.prange { display: flex; gap: 6px; }
.prange button {
  min-height: 38px; padding: 0 15px; border-radius: var(--r-pill);
  border: 1px solid var(--glass-rim); background: rgba(255,255,255,0.55);
  color: var(--plum-soft); font-size: 13.5px; font-weight: 700;
}
.prange button[aria-pressed="true"] {
  background: #fff; color: var(--accent-ink); box-shadow: var(--shadow-card);
}

/* ------------------------------------------------------------------ charts */
.chart { width: 100%; display: block; overflow: visible; }
.axis  { font-size: 10px; fill: var(--plum-soft); font-variant-numeric: tabular-nums; }
.gridline { stroke: var(--plum-mute); stroke-opacity: 0.30; stroke-width: 1; }
.baseline { stroke: var(--plum-mute); stroke-opacity: 0.55; stroke-width: 1; }

/* 4 legend squares, values included — a legend without values makes the
   reader hunt for the number that is the whole point. */
.legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 11.5px; color: var(--plum-soft); margin-bottom: 8px; }
.legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; margin-right: 5px; vertical-align: -1px; }

/* --------------------------------------------------------------- skeleton */
.skel { background: rgba(147,137,181,0.16); border-radius: var(--r-sm); animation: sk 1.4s ease-in-out infinite; }
@keyframes sk { 0%,100% { opacity: 0.5 } 50% { opacity: 0.85 } }

/* ------------------------------------------------------------------ empty */
.empty {
  padding: 22px 16px; text-align: center; border-radius: var(--r-md);
  background: rgba(255,255,255,0.45); border: 1px dashed var(--glass-rim);
  font-size: 13px; color: var(--plum-soft); line-height: 1.55;
}

/* ------------------------------------------------------------------ foot */
.pfoot {
  margin-top: 34px; padding-top: 16px; border-top: 1px solid var(--glass-rim);
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  font-size: 11.5px; color: var(--plum-soft);
}
.pfoot code { font-size: 11px; background: rgba(255,255,255,0.6); padding: 1px 5px; border-radius: 4px; }

/* ------------------------------------------------------------ responsive
   Breakpoints match the site's existing audit: 1080 / 980 / 720 / 560. */
@media (max-width: 1080px) {
  .c4 { grid-column: span 6; }
  .c8 { grid-column: span 12; }
}
@media (max-width: 720px) {
  .pshell { padding: 0 16px 60px; }   /* 16px gutter, per the audit */
  .ptop-in { padding: 10px 16px; }
  .c4, .c6, .c8 { grid-column: 1 / -1; }
  /* At 768 the collapsed one-column cards stretched to ~90 characters a line.
     Capped so they stay near 76. */
  .pgrid > .card2 { max-width: 620px; margin-inline: auto; width: 100%; }
  .bignum { font-size: 34px; }
}

@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; }
  .chart * { transition: none !important; animation: none !important; }
}

/* ============================================================================
   APP SHELL — top bar + persistent left sidebar.

   The phone has four tabs because a thumb reaches four things. A laptop can
   show the navigation AND the content at once, so it does.
   ========================================================================= */

.playout {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  max-width: 1480px;
  margin: 0 auto;
  align-items: start;
}

/* ------------------------------------------------------------------- top */
.ptop { position: sticky; top: 0; z-index: 50; }
.ptop-in {
  max-width: 1480px; margin: 0 auto;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 22px;
  background: rgba(255,255,255,0.74);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid var(--glass-rim);
}
.ptop-title { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ptop-h { font-size: 15px; font-weight: 750; letter-spacing: -0.015em; }
.ptop-sub { font-size: 12px; color: var(--plum-soft); }
.ptop-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* burger — hidden until the sidebar collapses */
.navtoggle { position: absolute; opacity: 0; pointer-events: none; }
.burger { display: none; width: 38px; height: 38px; border-radius: var(--r-sm);
          align-items: center; justify-content: center; color: var(--plum); cursor: pointer; }
.burger svg { width: 22px; height: 22px; }
.burger:hover { background: rgba(255,255,255,0.6); }

/* --------------------------------------------------------------- sidebar */
.pside { position: sticky; top: 59px; align-self: start; max-height: calc(100vh - 59px); overflow-y: auto; }
.pside-in { padding: 18px 12px 40px; display: flex; flex-direction: column; gap: 2px; }

.pside-kid {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 10px 12px; margin-bottom: 8px;
  border-bottom: 1px solid var(--glass-rim);
}
.pside-kid-av {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  background: var(--dusk-accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 14px;
}
.pside-kid-name { font-size: 13.5px; font-weight: 750; letter-spacing: -0.01em;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.pside-group {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent-ink); padding: 14px 10px 6px;
}

.pnav {
  display: flex; align-items: center; gap: 10px;
  min-height: 38px; padding: 0 10px; border-radius: var(--r-sm);
  font-size: 13.5px; font-weight: 650; color: var(--plum-soft);
}
.pnav:hover { background: rgba(255,255,255,0.58); color: var(--plum); }
.pnav:focus-visible { outline: 3px solid var(--dusk-accent, #7563C2); outline-offset: 2px; }
.pnav[aria-current="page"] {
  background: #fff; color: var(--accent-ink);
  box-shadow: var(--shadow-card);
}
.nicon { width: 17px; height: 17px; flex: none; }

/* ------------------------------------------------------------------ main */
.pmain { padding: 20px 22px 90px; min-width: 0; }

/* ------------------------------------------------------------- dashboard
   A denser grid than Reports: the point of the big screen is more at once. */
.dash { display: grid; gap: 14px; grid-template-columns: repeat(12, minmax(0, 1fr)); align-items: start; }
.dash > .card2 { margin: 0; }

/* A hero row that spans and reads first. */
.hero {
  grid-column: 1 / -1;
  display: grid; gap: 14px;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr) minmax(0, 1fr);
}
.hero > * { height: 100%; }

/* A compact stat used inside hero cards. */
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-v { font-size: 26px; font-weight: 800; letter-spacing: -.03em;
          font-variant-numeric: tabular-nums; line-height: 1.05; }
.stat-k { font-size: 11.5px; color: var(--plum-soft); }

/* An inline list of label/value rows, used by several cards. */
.rows { margin: 10px 0 0; }
.rows > div { display: flex; gap: 10px; padding: 7px 0; border-top: 1px solid rgba(147,137,181,0.22); }
.rows dt, .rows .k { font-size: 12.5px; color: var(--plum-soft); }
.rows dd, .rows .v { margin: 0; margin-left: auto; font-size: 13px; font-weight: 700;
                     font-variant-numeric: tabular-nums; text-align: right; }

/* --------------------------------------------------------- schedule rows */
.sched { margin-top: 12px; display: flex; flex-direction: column; }
.srow {
  display: grid; grid-template-columns: 74px 22px minmax(0, 1fr) auto;
  gap: 10px; align-items: start; padding: 10px 0;
  border-top: 1px solid rgba(147,137,181,0.22);
}
.srow-time { font-size: 13px; font-weight: 750; font-variant-numeric: tabular-nums; }
.srow-rail { position: relative; display: flex; justify-content: center; }
.srow-dot { width: 11px; height: 11px; border-radius: 50%; margin-top: 4px; flex: none;
            border: 2px solid var(--glass-strong); }
.srow-what { font-size: 13.5px; font-weight: 650; letter-spacing: -0.01em; }
.srow-why { font-size: 12px; color: var(--plum-soft); line-height: 1.45; margin-top: 2px; }
.srow.done .srow-time, .srow.done .srow-what { color: var(--plum-soft); }
.srow.active { background: rgba(255,255,255,0.5); border-radius: var(--r-sm); }

/* ------------------------------------------------------------ responsive */
@media (max-width: 1100px) {
  .hero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (max-width: 980px) {
  .playout { grid-template-columns: minmax(0, 1fr); }
  .burger { display: flex; }
  /* Collapsed: the sidebar becomes a disclosure under the top bar. */
  .pside {
    position: static; max-height: none; overflow: visible;
    display: none; border-bottom: 1px solid var(--glass-rim);
    background: rgba(255,255,255,0.8);
  }
  /* Sibling selector, no :has() — the toggle sits at body level before
     .playout, so this works on every engine and without JS. */
  .navtoggle:checked ~ .playout .pside { display: block; }
  .pside-in { flex-direction: row; flex-wrap: wrap; gap: 6px; padding: 12px 18px; }
  .pside-kid { width: 100%; margin: 0; border-bottom: none; padding-bottom: 4px; }
  .pside-group { width: 100%; padding: 6px 2px 2px; }
  .pnav { padding: 0 12px; }
  .hero { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
  .pmain { padding: 16px 16px 70px; }
  .ptop-in { padding: 10px 16px; }
  .ptop-title { display: none; }     /* the burger + brand is enough at 375 */
  .dash > .card2 { grid-column: 1 / -1 !important; max-width: none; }
  .srow { grid-template-columns: 62px 18px minmax(0, 1fr); }
  .srow > :nth-child(4) { grid-column: 3; }
}

/* ============================================================================
   ROLES, NOT UNIFORM CARDS.
   "When every element is a card, none is, and a parent cannot tell a control
   from a fact from a decision." Four treatments, spent by role:

     .r-hero   the ONE decision on the screen — accent rim + float. Max one.
     .r-data   a chart or a figure — glass, hairline, no float
     .r-ref    reference material — flat on the gradient, NO card at all
     .r-note   a footnote — plain text, no surface
   ========================================================================= */

.r-hero {
  background: var(--glass-strong);
  border: 1.5px solid rgba(117,99,194,0.55);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  padding: 20px 20px 18px;
  min-width: 0; overflow-wrap: anywhere;
}
.r-hero > h3 { font-size: 12px; font-weight: 800; letter-spacing: .1em;
               text-transform: uppercase; color: var(--accent-ink); margin-bottom: 6px; }

.r-data {
  background: var(--glass-strong);
  border: 1px solid var(--glass-rim);
  border-radius: var(--r-lg);
  box-shadow: none;
  padding: 16px 18px 14px;
  min-width: 0; overflow-wrap: anywhere;
}
.r-data > h3 { font-size: 14.5px; font-weight: 700; letter-spacing: -.015em; margin-bottom: 2px; }

/* Reference sits ON the gradient. No fill, no border, no shadow — it is
   context, not an object. */
.r-ref {
  background: none; border: none; box-shadow: none;
  padding: 4px 2px 10px; min-width: 0;
}
.r-ref > h3 { font-size: 11.5px; font-weight: 800; letter-spacing: .12em;
              text-transform: uppercase; color: var(--accent-ink); margin-bottom: 6px; }

.r-note { font-size: 12px; color: var(--plum-soft); line-height: 1.55;
          padding: 2px 2px 8px; }

/* THE MATH LINE — how a number was derived. Monospace, one line, not prose.
   This replaces the explainer paragraphs. */
.math {
  margin-top: 10px; padding-top: 9px;
  border-top: 1px solid rgba(147,137,181,0.26);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; line-height: 1.5; color: var(--plum-soft);
  overflow-wrap: anywhere;
}

/* Controls share ONE surface with hairlines between them, rather than each
   getting its own card. */
.ctrls {
  display: flex; align-items: center; gap: 0; flex-wrap: wrap;
  background: rgba(255,255,255,0.5);
  border: 1px solid var(--glass-rim);
  border-radius: var(--r-pill);
  padding: 3px;
}
.ctrls > * + * { border-left: 1px solid var(--glass-rim); }
.ctrls button {
  min-height: 34px; padding: 0 14px; border: none; background: transparent;
  border-radius: var(--r-pill); font-size: 13px; font-weight: 700;
  color: var(--plum-soft);
}
.ctrls button[aria-pressed="true"] { background: #fff; color: var(--accent-ink); }

/* A figure that needs no card: big number on the gradient. */
.figure { display: flex; flex-direction: column; gap: 2px; padding: 2px; }
.figure-v { font-size: 30px; font-weight: 800; letter-spacing: -.035em;
            font-variant-numeric: tabular-nums; line-height: 1; color: var(--plum); }
.figure-k { font-size: 11.5px; font-weight: 650; color: var(--plum-soft); }

/* ============================================================================
   SECTION PANELS — the 14-boxes fix, applied to Reports.

   Reports is a chart catalogue: 20 figures is the content, not a mistake. But
   giving each one its own fill, border, radius and shadow is, because then the
   SECTION stops reading as a thing and the page is 20 equal objects.

   So a section is ONE surface and its charts are separated by hairlines —
   exactly rule 1 from the wake-window rework ("controls share one surface with
   hairlines between them rather than one card each"), applied to data instead
   of controls. 20 boxes -> 7 panels.
   ========================================================================= */

.panel {
  grid-column: 1 / -1;
  background: var(--glass-strong);
  border: 1px solid var(--glass-rim);
  border-radius: var(--r-lg);
  padding: 2px 20px 8px;
  min-width: 0;
}

/* Inside a panel the charts keep their layout columns but lose their chrome —
   the panel is the object now. */
.panel-in {
  display: grid; gap: 0 24px;
  grid-template-columns: repeat(12, minmax(0, 1fr));
}
.panel-in > .r-data,
.panel-in > .r-ref {
  background: none; border: none; box-shadow: none;
  border-radius: 0;
  padding: 16px 0 14px;
  border-top: 1px solid rgba(147,137,181,0.26);
}
/* The first row in a panel has the heading above it, so no rule. */
.panel-in > .r-data:first-child,
.panel-in > .r-ref:first-child { border-top: none; }
/* Two cards sharing a row share the rule above them, not one each. */
.panel-in > .r-ref > h3 { font-size: 14.5px; font-weight: 700; letter-spacing: -.015em;
                          text-transform: none; color: var(--plum); }

.panel-head {
  display: flex; align-items: center; gap: 12px; justify-content: space-between;
  flex-wrap: wrap;
  padding: 16px 0 6px;
}
.panel-head h2 {
  font-size: 11.5px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent-ink);
}

@media (max-width: 720px) {
  .panel { padding: 2px 16px 8px; }
  .panel-in > .r-data, .panel-in > .r-ref { grid-column: 1 / -1 !important; max-width: none; }
}

/* ============================================================================
   VISUAL AUDIT FIXES — 2026-10-26, found by looking at every page.
   ========================================================================= */

/* 🔴 A NUMBER MUST NEVER BREAK. `.card2`/`.r-*` carry
   `overflow-wrap: anywhere` so monospace function names in the math line can
   wrap — but it also applied to the big figures, and in a squeezed hero column
   "7:00pm" rendered as "7:" / "0" / "0" / "p" / "m", one character per line.
   Numbers opt out and shrink instead. */
.bignum, .figure-v, .stat-v, .srow-time, .rows dd, .rows .v {
  overflow-wrap: normal; word-break: keep-all; white-space: nowrap;
}

/* THE HERO ROW.
   It was three columns; under ~1240px that left ~170px per column, narrower
   than the figure it has to hold, so "7:00pm" rendered one character per line.
   Two columns by default, with the hero spanning both — it is the decision, it
   should lead.

   🔴 The children carry their own span classes (c4/c8) from card({span}), which
   are written for the 12-column .dash grid and are meaningless inside a 2- or
   3-column hero. They have to be neutralised here or they overflow the row —
   that is what kept the column squeezed after the first fix. */
.hero { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.hero > *,
.hero > .c4, .hero > .c6, .hero > .c8, .hero > .c12 { grid-column: auto; max-width: none; }
.hero > .r-hero { grid-column: 1 / -1; }
@media (min-width: 1240px) {
  .hero > .r-hero { grid-column: auto; }
}

/* The math line was clipping at the card edge. Let it wrap and never clip. */
.math { white-space: normal; overflow-wrap: anywhere; }

/* One-row banner. "honesty banners longer than one row" is on the cut list,
   and this was six lines on a phone. */
.pbanner {
  align-items: center;
  padding: 9px 14px;
  font-size: 12.5px;
  line-height: 1.45;
}
.pbanner .b-more { color: var(--accent-ink); text-decoration: underline; cursor: help; }

/* A hero holds a decision, not an essay. Clamp any supporting prose in it. */
.r-hero .sub {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Filters share ONE surface, like every other control group. */
.filters { display: flex; flex-wrap: wrap; gap: 0; }

/* The compliance chip on Schedule was unstyled green text. */
.chip-plan {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 7px 13px; border-radius: var(--r-pill);
  background: rgba(62,156,122,0.14);
}
.chip-plan b { font-size: 15px; font-weight: 800; color: var(--cta-ink);
               font-variant-numeric: tabular-nums; }
.chip-plan span { font-size: 10.5px; font-weight: 800; letter-spacing: .1em;
                  text-transform: uppercase; color: var(--cta-ink); }

/* Day stepper — real buttons, not bare glyphs. */
.daynav { display: flex; align-items: center; gap: 2px; }
.daynav button {
  width: 38px; height: 38px; border-radius: var(--r-sm);
  border: 1px solid var(--glass-rim); background: rgba(255,255,255,0.55);
  color: var(--plum); font-size: 17px; line-height: 1;
}
.daynav button:hover { background: #fff; }
.daynav-lbl { text-align: center; min-width: 124px; padding: 0 6px; }

/* Plan buttons on the gate were inconsistent — one filled, one bare text. */
.btn-plan {
  margin-top: 12px; width: 100%; min-height: 44px; border-radius: var(--r-pill);
  font-weight: 750; font-size: 14px; border: 1px solid var(--glass-rim);
  background: rgba(255,255,255,0.7); color: var(--accent-ink);
}
.btn-plan.is-primary { background: var(--cta); color: #fff; border-color: transparent; }

/* A disabled primary must LOOK disabled. A filled green "Not available yet"
   reads as the main call to action and then refuses the click. */
.btn-plan[disabled] {
  background: rgba(147,137,181,0.18); color: var(--plum-soft);
  border-color: var(--glass-rim); cursor: not-allowed;
}
.btn-plan[disabled].is-primary { background: rgba(147,137,181,0.18); color: var(--plum-soft); }

/* ============================================================ onboarding
   The app's stepped flow, one screen at a time, responsive to web.

   The app shows one screen because a phone must. Here the sequence is the same
   and each stage simply gets its real width — the ladder, the plan and the
   journey render at full size instead of cropped to 390px. Centred in a single
   column so the eye never has to hunt for the next step.
   ========================================================================= */
.onb-wrap { max-width: 760px; margin: 0 auto; padding: 0 20px 72px; }

.onb-screen { display: flex; flex-direction: column; min-height: calc(100vh - 120px); }

.onb-progress {
  height: 3px; border-radius: 2px; background: rgba(147,137,181,0.26);
  margin: 18px 0 14px; overflow: hidden;
}
.onb-progress > span { display: block; height: 100%; background: var(--dusk-accent);
                       transition: width .35s ease; }

.onb-back {
  align-self: flex-start; background: none; border: none; padding: 4px 0;
  color: var(--plum-soft); font-size: 13px; font-weight: 650;
}
.onb-back:hover { color: var(--accent-ink); }

.onb-rail { margin: 6px auto 4px; max-width: 520px; width: 100%; }

.onb-head { padding: 10px 0 18px; }
.onb-eyebrow {
  font-size: 11.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-ink); margin-bottom: 7px;
}
.onb-title { font-size: clamp(26px, 4vw, 38px); letter-spacing: -.035em; line-height: 1.06; }
.onb-sub { margin-top: 10px; font-size: 16px; line-height: 1.5; color: var(--plum);
           max-width: 52ch; }

.onb-stage { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center;
             padding: 4px 0 20px; min-height: 0; }

.onb-card {
  background: var(--glass-strong); border: 1px solid var(--glass-rim);
  border-radius: var(--r-lg); padding: 16px 18px; min-width: 0; overflow-wrap: anywhere;
}
.onb-card-bare { background: none; border: none; padding: 0; }
.onb-hero { max-width: 100%; }

.onb-nextside { display: flex; align-items: baseline; gap: 10px; padding-bottom: 10px;
                border-bottom: 1px solid rgba(147,137,181,0.26); margin-bottom: 4px; }
.onb-side { font-size: 20px; font-weight: 800; letter-spacing: -.02em; color: var(--accent-ink); }
.onb-woke { padding-bottom: 12px; }

.onb-map { display: flex; flex-direction: column; gap: 2px; }
.onb-map-step {
  display: flex; gap: 13px; align-items: flex-start; padding: 13px 0;
  border-top: 1px solid rgba(147,137,181,0.26);
}
.onb-map-step:first-child { border-top: none; }
.onb-map-n {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--dusk-accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 800;
}
.onb-map-step.is-goal .onb-map-n { background: var(--sleep-night); color: #E8C475; }

.onb-foot { display: flex; flex-direction: column; align-items: center; gap: 8px;
            padding-top: 6px; }
.onb-cta {
  width: 100%; max-width: 420px; min-height: 50px; border-radius: var(--r-pill);
  background: var(--cta); color: #fff; border: none;
  font-size: 15.5px; font-weight: 750; letter-spacing: -.01em;
}
.onb-cta:hover { filter: brightness(1.04); }
.onb-cta[disabled] { background: rgba(147,137,181,0.24); color: var(--plum-soft);
                     cursor: not-allowed; filter: none; }
.onb-alt { font-size: 13.5px; font-weight: 650; color: var(--accent-ink);
           text-decoration: underline; padding: 6px; }

.ob-fields { display: flex; gap: 14px; flex-wrap: wrap; }
.ob-fields label { display: flex; flex-direction: column; gap: 5px; flex: 1 1 200px; min-width: 0; }
.ob-fields span { font-size: 11.5px; font-weight: 800; letter-spacing: .08em;
                  text-transform: uppercase; color: var(--accent-ink); }
.ob-fields input {
  min-height: 48px; padding: 0 14px; font-size: 16px;   /* 16px: iOS zooms below it */
  border-radius: var(--r-md); border: 1px solid var(--glass-rim);
  background: #fff; color: var(--plum); font-family: inherit;
}
.ob-fields input:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(117,99,194,0.35); }

@media (max-width: 720px) {
  .onb-wrap { padding: 0 16px 56px; }
  .onb-stage { justify-content: flex-start; }
}

.c5 { grid-column: span 5; }
.c7 { grid-column: span 7; }
@media (max-width: 1080px) { .c5, .c7 { grid-column: 1 / -1; } }
/* THE COMPARISON TABLE HAD NO BASE RULES. Only the max-width:720 block below
   ever set grid-template-columns, so above 720 the cells were inline <span>s
   and the whole table rendered as run-on prose — "What you fill inAn intake
   form, onceName and birthdate, once". It has never worked at desktop width.
   Three columns: the question, them, us. */
.ob-vs { margin-top: 12px; border-top: 1px solid var(--glass-rim); }
.ob-vs-head,
.ob-vs-row {
  display: grid;
  grid-template-columns: minmax(130px, 1.1fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 14px;
  align-items: baseline;
  padding: 9px 0;
  border-bottom: 1px solid var(--glass-rim);
}
.ob-vs-head {
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--plum-mute);
  padding-bottom: 7px;
}
.ob-vs-head .is-us { color: var(--accent-ink); }
.ob-vs-k { font-size: 13px; font-weight: 700; color: var(--plum); }
.ob-vs-them { font-size: 13px; line-height: 1.45; color: var(--plum-soft); }
/* The column that is the argument gets the ink and the weight. */
.ob-vs-us { font-size: 13px; line-height: 1.45; font-weight: 650; color: var(--accent-ink); }
.ob-vs-row:last-child { border-bottom: 0; }

@media (max-width: 720px) {
  .ob-vs-head { display: none; }
  .ob-vs-row { grid-template-columns: 1fr; gap: 3px; }
  .ob-vs-them::before { content: 'Consultant: '; color: var(--plum-mute); }
  .ob-vs-us::before { content: 'Cradle: '; color: var(--accent-ink); }
}

/* A featured option: accent rim, no float. The float is reserved for the one
   decision on the page (.r-hero) and stops meaning anything if two things have it. */
.is-featured { border: 1.5px solid rgba(117,99,194,0.55); }

/* ------------------------------------------------------------- milestones
   Three columns: what it is, where the baby sits in the typical range (drawn),
   and the status. The range as a bar reads in a glance; "weeks 8 to 16" as
   text does not. */
.ms-list { margin-top: 10px; }
.ms-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 120px 118px;
  gap: 14px; align-items: center; padding: 11px 0;
  border-top: 1px solid rgba(147,137,181,0.26);
}
.ms-row:first-child { border-top: none; }
.ms-main { min-width: 0; }
.ms-range { display: flex; flex-direction: column; gap: 4px; }
.ms-track { height: 7px; border-radius: 4px; background: rgba(147,137,181,0.22); overflow: hidden; }
.ms-fill { height: 100%; border-radius: 4px; }
.ms-stat { text-align: right; }
@media (max-width: 860px) {
  .ms-row { grid-template-columns: minmax(0, 1fr) auto; }
  .ms-range { grid-column: 1; }
  .ms-stat { grid-row: 1; grid-column: 2; }
}

/* ----------------------------------------------------------- activity log
   The rows were `68px | 12px | 150px | 1fr`, which put the detail a third of
   the screen from the label it described. Tight time + dot + description, with
   the detail right-aligned where the eye scans for numbers. */
.logtable { margin-top: 8px; }

.log-day {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 14px 0 5px; border-bottom: 1px solid rgba(147,137,181,0.34);
}
.log-day-d { font-size: 12px; font-weight: 800; letter-spacing: .08em;
             text-transform: uppercase; color: var(--accent-ink); }
.log-day-sum { margin-left: auto; }

.logrow {
  display: grid; grid-template-columns: 62px 10px minmax(84px, auto) minmax(0, 1fr);
  gap: 9px; align-items: baseline;
  padding: 5px 0; border-bottom: 1px solid rgba(147,137,181,0.16);
}
.logrow:hover { background: rgba(255,255,255,0.4); }
.logrow-t { font-size: 12px; color: var(--plum-soft); font-variant-numeric: tabular-nums; }
.logrow-dot { width: 7px; height: 7px; border-radius: 50%; align-self: center; }
.logrow-w { font-size: 13px; font-weight: 650; }
.logrow-d { font-size: 12px; color: var(--plum-soft); text-align: right;
            overflow-wrap: anywhere; }
.logrow.is-running .logrow-d { color: var(--accent-ink); font-weight: 700; }

/* The summary as a strip of figures on the gradient — not a quarter-width card
   holding three numbers while the log is cramped beside it. */
.logstrip { display: flex; flex-wrap: wrap; gap: 26px; align-items: baseline; padding: 4px 2px 10px; }

@media (max-width: 720px) {
  .logrow { grid-template-columns: 54px 10px minmax(0, 1fr); }
  .logrow-d { grid-column: 3; text-align: left; }
  .logstrip { gap: 18px; }
}

/* ============================================================================
   COHESION PASS — 2026-10-26.

   The complaint: "white space, purple space, gaps, it doesn't look like a
   cohesively put together site." Three structural causes, not styling taste:

   1. The content column stopped at its last card while the sidebar ran full
      height, so the bottom half of every thin page was bare gradient with a
      footer floating in the middle of it.
   2. Every page opened differently — some with a banner, some with a filter
      bar, some straight into a card — so nothing established "this is the same
      site" above the fold.
   3. Three different gap scales (dash 14, hero 14, panel padding 2/20/8) meant
      no vertical rhythm carried between sections.
   ========================================================================= */

/* 1. The main column is as tall as its content. 🔴 It USED to be forced to
      viewport height with the footer pinned to its bottom, which was a fix for
      a real problem (a footer floating mid-page) that then CAUSED a worse one:
      up to 554px of bare gradient between the last card and that pinned footer
      on a short page. A page is allowed to end. Pinning the footer to the fold
      only guarantees a gap whenever the content does not reach it. */
.pmain { display: flex; flex-direction: column; }

/* 2. One page-header band, the same on every page. */
.phead {
  display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap;
  padding: 4px 0 14px;
  border-bottom: 1px solid var(--glass-rim);
  margin-bottom: 18px;
}
.phead-t { min-width: 0; }
.phead-t h1 {
  font-size: clamp(22px, 2.6vw, 28px); letter-spacing: -.03em; line-height: 1.1;
}
.phead-t p { font-size: 13px; color: var(--plum-soft); margin-top: 4px; max-width: 62ch; }
/* Key figures sit in the header band, so every page opens with numbers rather
   than with a card edge. */
.phead-stats { display: flex; gap: 22px; flex-wrap: wrap; margin-left: auto; align-items: baseline; }
.phead-stats .figure-v { font-size: 22px; }
.phead-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* 3. One gap scale. */
.dash { gap: 16px; }
.hero { gap: 16px; }
.panel { padding: 0 20px 10px; }
.panel-in { gap: 0 24px; }

/* The banner belongs above the header band, tight to it. */
.pbanner { margin: 0 0 14px; }

@media (max-width: 720px) {
  .phead { padding-bottom: 12px; margin-bottom: 14px; }
  .phead-stats { margin-left: 0; gap: 16px; }
  .panel { padding: 0 16px 10px; }
}

/* A 28-day habit strip. A streak is the honest measure of whether a daily
   prompt is working, and 28 squares read faster than "16 of 28 days". */
.habit { display: grid; grid-template-columns: repeat(14, 1fr); gap: 4px; margin-top: 10px; }
.habit-cell {
  aspect-ratio: 1; border-radius: 3px;
  background: rgba(147,137,181,0.22);
}
.habit-cell.is-on { background: var(--dusk-accent); }

/* ============================================================================
   RAGGED ROWS — the fix for the holes.

   Cards sat in a grid row at their natural heights, so a 157px card beside a
   430px one left 273px of bare gradient under it. On Schedule at 1440 the worst
   hole was 594px. A grid row is the wrong container for content of wildly
   different length.

   Two layers:
     1. `align-items: stretch` everywhere, so a card can never be shorter than
        its row. This alone removes every gradient hole.
     2. COLUMN PACKING for the dashboards. Stretching a short card just moves
        the emptiness inside it. Cards that are text lists belong in a rail
        where they stack beneath each other and the next one starts immediately;
        charts belong in the wide column. Each column flows, so nothing can
        leave a hole.
   ========================================================================= */

.dash { align-items: start; }          /* natural heights — see below */
.hero, .panel-in { align-items: stretch; }
.dash > *, .hero > *, .panel-in > * { min-width: 0; }

/* Two columns that each FLOW, rather than one grid that rags. */
.cols {
  grid-column: 1 / -1;
  display: grid; gap: 16px;
  grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr);
  align-items: start;
}
.col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* Inside a column the span classes are meaningless — the column IS the width. */
.col > * { grid-column: auto !important; max-width: none; }

@media (max-width: 1080px) {
  .cols { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================================
   DEAD SPACE — the second half of the ragged-row problem.

   Making cards stretch to their row removed the gradient holes BETWEEN them and
   moved the emptiness INSIDE the short one: up to 280px of blank card under the
   content. And pinning the footer to the bottom of the viewport left up to
   291px of nothing above it on a short page.

   Both are the same mistake — a container that grew while its content stayed
   pinned to the top. The fix is to let content own the height it is given.
   ========================================================================= */

/* 1. Every card is a column, and its provenance line sits at the BOTTOM.
      Extra height then becomes space between the figure and the footnote —
      which reads as a card — instead of a dead band under everything. */
.r-data, .r-ref, .r-hero {
  display: flex; flex-direction: column;
}
.r-data > .math, .r-ref > .math, .r-hero > .math { margin-top: auto; }

/* A chart takes the slack rather than leaving it. The viewBox keeps the
   aspect ratio, so it scales and centres instead of distorting. */
.r-data > svg.chart, .r-hero > svg.chart { flex: 1 1 auto; min-height: 0; }

/* 2. Nothing stretches to fill a page height any more — see above. */

/* A note or a control bar must NOT absorb that slack — only real content
   should grow. */
.pmain > .dash > .r-note,
.pmain > .dash > .c12:has(> .ctrls),
.pmain > .dash > .phead { flex: 0 0 auto; }

/* 3. In the hero row specifically, the three cards hold very different amounts.
      Space their blocks out so the tallest sets the height and the others read
      as deliberately airy rather than top-heavy with a dead foot. */
.hero > * > .sub { flex: 0 0 auto; }


/* ============================================================================
   STRETCH WAS A BAND-AID. PACKING IS THE FIX.

   Forcing equal heights on genuinely unequal content just relocates the
   emptiness: Account's four list cards went from ragged rows to 225-276px of
   dead card each. `margin-top: auto` on the provenance line only helps a card
   that HAS one.

   So .dash goes back to natural heights, and EVERY multi-card page lays out in
   flowing columns instead. A column has no rows to rag and no height to pad —
   the next card simply starts where the last one ended. Stretch survives only
   where the contents genuinely fill it: the hero banner row, and the Reports
   panels, whose children are charts.
   ========================================================================= */

/* A column set that works for 2 or 3 columns, balanced by content weight. */
.cols-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1080px) { .cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) { .cols-even, .cols-3 { grid-template-columns: minmax(0, 1fr); } }


/* The hero banner's three cards should be compact and equal. Next-up carried a
   four-line wind-down paragraph that made it 161px taller than its neighbours,
   and stretching them to match put that 161px of nothing under each. One line
   in the banner; the full text is on Schedule and in the hover. */
.hero .r-hero .sub, .hero .r-data .sub {
  -webkit-line-clamp: 1; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;
}

/* The staleness banner. Amber rather than the sample banner's violet: one says
   "this is not a real baby", the other says "this is your baby but the feed has
   stopped", and a parent must not have to read carefully to tell those apart. */
.pbanner.is-stale {
  background: #FFF6E6;
  border-left-color: var(--warning, #E0A33A);
  color: #6B4D14;
}
.pbanner.is-stale b { color: #4A350A; }

/* Scroll note, kept because I got this wrong once and may again: the portal
   pages inherit `overflow-x: clip` on <body> from styles.css:84, and
   `scroll-behavior: smooth` on <html>. Programmatic scrolling WORKS — but it is
   ASYNCHRONOUS, so reading window.scrollY immediately after scrollTo() or
   scrollIntoView() returns the old value and looks like a no-op. It is not one.
   I diagnosed a scroll bug from exactly that, and briefly shipped an override
   for it. Wait a frame before measuring. */

/* The gate's ground is white, continuous with the onboarding it follows.
   The lavender sky stays on the data pages, where it is the app's own dusk. */
body.gate-ground { background: #FFFFFF; }
body.gate-ground .pshell { background: transparent; }

/* ═══════════════════════════ the tour ═══════════════════════════════════
   A page-level spotlight. The dim is ONE enormous box-shadow spread on the
   ring itself rather than a second overlay element — so the hole tracks the
   ring's geometry for free instead of being a second animated thing that can
   fall out of step with it. Borrowed from scene.js, which learned it from the
   app's evenodd path. */
.tr-layer { position: fixed; inset: 0; z-index: 900; pointer-events: none; }
/* Deliberately NOT scroll-locked. The usual modal `overflow: hidden` would
   make the tour unable to scroll a target into view, which is most of its job;
   the ring tracks scrolling instead (see tour.js's scroll listener). */

.tr-ring {
  position: fixed; border-radius: 22px; pointer-events: none;
  border: 2.5px solid #BFB6E6;
  box-shadow: 0 0 0 9999px rgba(16, 25, 58, 0.58);
  transition: left .34s cubic-bezier(.22,.61,.36,1), top .34s cubic-bezier(.22,.61,.36,1),
              width .34s cubic-bezier(.22,.61,.36,1), height .34s cubic-bezier(.22,.61,.36,1),
              opacity .22s linear;
}
/* With no target there is nothing to punch out, so the dim becomes a plain
   full-bleed wash — otherwise a 0x0 ring leaves a bright pinhole. The state is
   set explicitly by tour.js (setRing), not sniffed out of the style attribute:
   the geometry comes from there too, so this rule only drops the border. */
.tr-ring[data-blank] { border-color: transparent; }

.tr-card {
  position: fixed; pointer-events: auto; width: min(360px, calc(100vw - 32px));
  background: #FFFFFF; border-radius: var(--r-xl); padding: 22px 24px;
  box-shadow: 0 24px 64px rgba(16, 25, 58, 0.4);
}
.tr-card[data-placement="centre"] {
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(460px, calc(100vw - 32px));
}
/* Phone: dock it. A floating callout next to a full-width card has nowhere to
   sit, and a parent holding a phone one-handed wants the buttons near their
   thumb, not next to the card being described. */
.tr-card[data-placement="dock"] {
  left: 12px; right: 12px; bottom: 12px; top: auto; width: auto;
  max-height: 62vh; overflow-y: auto;
}
/* Same dock, other edge — used when the target is in the lower half, so the
   card never sits on top of what it is pointing at. */
.tr-card[data-placement="dock-top"] {
  left: 12px; right: 12px; top: 12px; bottom: auto; width: auto;
  max-height: 62vh; overflow-y: auto;
}

.tr-eyebrow {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: 11.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent-ink); margin-bottom: 10px;
}
.tr-count { color: var(--plum-soft); font-weight: 700; letter-spacing: .04em; }
.tr-title { font-size: 21px; line-height: 1.2; letter-spacing: -.025em; margin: 0; color: var(--plum); }
.tr-body { font-size: 15px; line-height: 1.55; color: var(--plum-soft); margin: 10px 0 0; }

.tr-actions { display: flex; align-items: center; gap: 10px; margin-top: 18px; }
.tr-next {
  flex: 1; min-height: 46px; border: 0; border-radius: var(--r-pill);
  background: #7563C2; color: #fff; font-family: inherit; font-size: 15.5px; font-weight: 700;
}
.tr-next:hover { background: #6553B2; }
.tr-back, .tr-skip {
  min-height: 46px; padding: 0 14px; border: 0; background: none;
  font-family: inherit; font-size: 14px; font-weight: 650; color: var(--plum-soft);
}
.tr-back:hover, .tr-skip:hover { color: var(--plum); }

/* The app step. Deliberately the densest card in the tour — it is the one
   whose instructions decide whether a paying parent reaches their data. */
.tr-app { margin-top: 16px; }
.tr-stores { display: flex; gap: 8px; flex-wrap: wrap; }
.tr-stores .store { padding: 8px 14px; }
.tr-steps { margin: 14px 0 0; padding-left: 20px; font-size: 14.5px; line-height: 1.65; color: var(--plum); }
.tr-steps b { font-weight: 700; }
.tr-warn {
  margin-top: 12px; padding: 12px 14px; border-radius: var(--r-md);
  background: #FFF6E6; border-left: 3px solid var(--warning);
  font-size: 13.5px; line-height: 1.5; color: #6B4D14;
}
.tr-warn b { color: #4A350A; }
/* Where Geoff's sign-in screenshots drop in. Empty until then, and collapsed
   so it leaves no gap. */
.tr-shots:empty { display: none; }
.tr-shots img { border-radius: var(--r-md); margin-top: 12px; box-shadow: var(--shadow-card); }

.tr-replay { background: none; border: 0; font-family: inherit; cursor: pointer;
             text-decoration: underline; text-underline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .tr-ring { transition: none; }
}
