/* ════════════════════════════════════════════════════════════════════════
   WHERE IS EVERYONE — the view's own stylesheet.

   Loaded after css/calendar.css, which is FROZEN at 12 sections and shared by
   two shipped, pixel-verified views (contract §4). Rules here:

     • class prefix `fc-where-*` only — no `fc-day-*`, no `fc-insp-*`, and
       `fc-sheet__*` belongs to the existing mobile day sheet, not to this file
     • sections numbered from 1, this file's own
     • this file declares NO `:root` block at all — every custom property it
       adds is scoped to `.fc-where` (SECTION 1, and the reasoning there). It
       therefore cannot redeclare any of the 227 names calendar.css owns across
       its SECTION 1, SECTION 1b and SECTION 10, and cannot put the
       token-mirror gate into drift. Verified mechanically with the contract §4
       `comm -12` command (prints nothing) and with
       `node scripts/token-mirror.check.mjs`.

   Every name added here is prefixed `--fc-w-`, which no calendar.css name uses
   — the same convention `--fc-y-` (Year) and `--fc-m-` (mobile) already
   follow.

   Where a global token already says exactly the same thing, it is REUSED
   rather than shadowed under a `--fc-w-` alias: `--fc-size-meta` (11px) for
   the sub-note, `Home` and the avatar initial, `--fc-size-rail-num` (15px) for
   the column numeral, `--fc-size-badge` (9px) for the column weekday, and
   `--fc-radius-barcut` (3px) for a band that runs off the range.

   Values: DESIGN-DECISIONS.md §1 (colour — the ONLY source; UI-RULES §2 is a
   known-wrong table and is not consulted), §3 (Where lanes), §5, §6, and
   FONTS.md §3 "Where is everyone".

   MOTION: genuinely undesigned estate-wide (`token-architecture.md` names the
   gap and no motion tier exists). One hardcoded `160ms ease-out` per contract
   §5, flagged on every line, and a reduced-motion block of this file's own in
   SECTION 9 — calendar.css SECTION 6 cannot know about selectors that did not
   exist when it was written.
   ════════════════════════════════════════════════════════════════════════ */


/* ════════════════════════════════════════════════════════════════════════
   SECTION 1 — WHERE PROPERTIES  (all on .fc-where — NOT one at :root)

   Every custom property this file declares is COMPONENT-SCOPED. Contract §4
   permits a view's own `:root` block, but the standing ruling across the four
   lanes is the Inspector's pattern: anything at `:root` is a global brand token
   and must be mirrored into brand-foundation/calendar.tokens.json, which no
   lane may edit — so a `:root` block here fails
   scripts/token-mirror.check.mjs by construction. The Day lane put 50 names at
   `:root` and failed with 50 css-only entries; the Inspector put 18 on
   `.fc-insp` and stayed green. This file follows the Inspector.

   SIX OF THESE ARE GENUINELY BRAND-LEVEL and are listed in the lane report as
   PROMOTION CANDIDATES for the integrator to mirror into the skin and then
   reference from here: --fc-w-home-lane, --fc-w-weekend-lane, --fc-w-overlay,
   --fc-w-state-shadow, --fc-w-glyph-empty, --fc-w-glyph-error. They sit
   alongside values already in DESIGN-DECISIONS §1/§6. Until they are promoted
   they live here, which is honest about their status rather than failing a
   gate to make a point.

   NOTE FOR ANYONE ADDING A JS READ: js/views/where.js's tokens() reads via
   getComputedStyle(document.documentElement), and that is correct ONLY because
   every property it reads (--fc-person-*, --fc-alpha-*) is declared in
   calendar.css's own :root. Nothing below is read from JS. If that ever
   changes, the read must move to the mounted node — a component-scoped
   property read off documentElement returns '' silently, and a colour simply
   vanishes with nothing thrown.

   Values from DESIGN-DECISIONS §1 (colour), §3 (Where lanes), §6 (states) and
   FONTS.md §3 "Where is everyone".
   ════════════════════════════════════════════════════════════════════════ */
.fc-where {
  /* ── brand-level: PROMOTED 2026-08-06 at integration ──────────────────
     The first three were proposed here as promotion candidates and are now
     brand tokens in calendar.css SECTION 1, mirrored in the token file. The
     local names stay as aliases so the promotion is one edit rather than one
     per rule. --fc-w-overlay, --fc-w-state-shadow and --fc-w-glyph-error were
     NOT promoted with them and remain this component's own. */
  --fc-w-home-lane:    var(--fc-home-lane);        /* "home is a STATE WITH A COLOUR, not an absence" */
  --fc-w-weekend-lane: var(--fc-weekend-lane);     /* §1 "Weekend: … Where lane" */
  /* The other four — --fc-w-overlay, --fc-w-state-shadow, --fc-w-glyph-empty
     and --fc-w-glyph-error — are GONE, with the empty/error card they served.
     That component is calendar.css SECTION 5's `.fc-state` now (see SECTION 5
     of this file). Their values live as --fc-state-overlay, --fc-shadow-state,
     --fc-glyph-tile-empty and --fc-breach-ground. */

  /* ── geometry (DESIGN-DECISIONS §3) ─────────────────────────────────── */
  --fc-w-gutter:      104px;  /* the person column: `104px repeat(7,minmax(0,1fr))` */
  --fc-w-lane-h:      46px;   /* lane height — clears the 24px AA floor and 44px */
  --fc-w-band-h:      26px;   /* the away band inside the 46px lane */
  --fc-w-radius-band: 13px;   /* a full pill at 26px tall */
  /* ── two trips on one person on one day (spec 08 §5, added 2026-08-06) ────
     The lane height is fixed at 46px by DESIGN-DECISIONS §3 and the grid has to
     keep lining up with Month, so a second band cannot make the lane taller.
     TWO TREATMENTS, on the same split as everything else in this file — whether
     the CELLS CARRY WORDS:

       bare (Month scope, mobile) — no words in the cell, so the 26px envelope
         divides evenly: two 12px pills, 2px apart, occupying exactly the 26px
         one band used to. Each still carries its own trip name.

       week scope — the cell carries `Travels to Morocco` in white ON the band,
         and dividing the envelope puts the 2px gap through the bottom of the
         13px location line, cutting the descenders against the white card on
         every away day in that lane. (Measured, not estimated: the numbers are
         on the --split rule below. The two pills are the same colour 2px apart,
         so the text keeps its ground — the defect is the 2px, and the 2px is
         enough.) So at week scope every run keeps its FULL band — all one
         person colour, so overlapping runs merge into a single continuous away
         region, which is exactly what is true — and NOTHING is drawn for the
         second trip. The words carry it: `Morocco +1` / `+ Travels to London`.
         A halo sub-stripe was built here and removed on 2026-08-06 as
         undecodable; the reasoning is on the rule it used to occupy. */
  --fc-w-band-gap:      2px;  /* between two bands sharing one lane */
  --fc-w-band-h-mobile: 22px; /* the mobile strip's bar envelope (SECTION 8) */
  --fc-w-band-bottom:   5px;  /* …and its offset from the cell floor */
  --fc-w-avatar:      26px;
  --fc-w-head-h:      44px;   /* the day-column header row */

  /* ── type (FONTS.md §3 "Where is everyone") ─────────────────────────────
     Only the sizes with no exact global equivalent. `Home`, the sub-note and
     the avatar initial are 11px (the global size-meta); the column numeral is
     15px (size-rail-num); the column weekday is 9px (size-badge). Those four
     reuse the global rather than aliasing it.
     NB: token names are written WITHOUT their leading dashes in this comment
     on purpose — the contract §4 collision grep matches `--fc-*` at the start
     of a line and cannot tell a comment from a declaration. */
  --fc-w-size-loc:      13px;   /* location line, 13/700 in the person's colour */
  --fc-w-size-name:     12px;   /* person name, 12/700 (800 when selected) */
  --fc-w-size-count:    46px;   /* the big Schengen numeral, 34–46/800/-0.04em */
  --fc-w-size-of:       13px;   /* `of 90`, 13/700 */
  --fc-w-size-caps:     10px;   /* micro caps label, 9–10.5/800/.06–.1em */
  --fc-w-track-count:   -0.04em;
  --fc-w-leading-count: 0.9;    /* FONTS §3: deliberate — "do not normalise it" */
}


/* ════════════════════════════════════════════════════════════════════════
   SECTION 2 — SHELL + CONTROLS
   ════════════════════════════════════════════════════════════════════════ */
.fc-where {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.fc-where__controls {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--fc-tap-desktop);
}

/* RangeSwitch — Week / Month, on the shared pill track */
.fc-where__range {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--fc-pill-track);
  border-radius: var(--fc-radius-pill);
}
.fc-where__range-btn {
  min-height: var(--fc-tap-desktop);
  padding: 5px 14px;
  border: 0;
  border-radius: var(--fc-radius-tag);
  background: transparent;
  color: var(--fc-text-muted);
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
  transition: background 160ms ease-out, color 160ms ease-out; /* TODO(motion-tokens) */
}
.fc-where__range-btn:hover { color: var(--fc-text-ink); }
.fc-where__range-btn.is-on {
  background: var(--fc-surface-card);
  color: var(--fc-text-ink);
  font-weight: var(--fc-weight-semibold);
  box-shadow: var(--fc-shadow-pill-active);
}

.fc-where__step { display: inline-flex; gap: 6px; }
.fc-where__step-btn {
  min-width: var(--fc-tap-desktop);
  min-height: var(--fc-tap-desktop);
  padding: 4px 10px;
  border: 1px solid var(--fc-outline-quiet);
  border-radius: var(--fc-radius-tag);
  background: var(--fc-surface-card);
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  cursor: pointer;
  transition: background 160ms ease-out, border-color 160ms ease-out; /* TODO(motion-tokens) */
}
.fc-where__step-btn:hover { background: var(--fc-rail-hover); border-color: var(--fc-divider); }

.fc-where__range-label {
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
}

/* The LOADING… pill GRADUATED, 2026-08-07. It was `.fc-where__loading-pill`
   and this file owned it; Year, Month and Day needed the identical pill the
   moment they grew a loading state, and a second transcription of
   DESIGN-DECISIONS §6's "10/800/.1em, #8A94A6 on #F1F4FA" is exactly how two
   copies come back. It is `.fc-loading-pill` in calendar.css SECTION 5 now,
   built by js/views/state-card.js. Nothing is redeclared here — a local alias
   for a component this file no longer owns is the same mistake in a smaller
   font. Its POSITION is Where's and needs no rule: the controls row is a flex
   line and the pill is the last item in it, which is where §6 puts it. */


/* ════════════════════════════════════════════════════════════════════════
   SECTION 3 — THE PERSON BOARD

   ONE grid template drives both scopes so the columns line up (spec 08 §3):
   `var(--fc-w-gutter) repeat(var(--fc-w-cols), minmax(0, 1fr))`. Only the
   column COUNT changes between Week (7) and Month (28–31). `minmax(0,1fr)`
   rather than `1fr` so a long location line cannot push a column wider than
   its share — equal columns regardless of content (HANDOFF §check-list).
   ════════════════════════════════════════════════════════════════════════ */
.fc-where__board { position: relative; }

.fc-where__grid {
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-grid);
  overflow: hidden;
  background: var(--fc-surface-card);
}

.fc-where__row {
  display: grid;
  grid-template-columns: var(--fc-w-gutter) repeat(var(--fc-w-cols), minmax(0, 1fr));
  align-items: stretch;
}
.fc-where__row--head { min-height: var(--fc-w-head-h); background: var(--fc-surface-rail); }
.fc-where__lane {
  min-height: var(--fc-w-lane-h);
  border-top: 1px solid var(--fc-hairline-row);
  position: relative;
}

/* ── the day-column header ─────────────────────────────────────────────── */
.fc-where__col {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 6px 2px;
  border-left: 1px solid var(--fc-cell-vertical);
  min-width: 0;
}
.fc-where__col--weekend { background: var(--fc-weekend-chip); }
.fc-where__col--today   { background: var(--fc-today-cell); }
.fc-where__col-num {
  font: var(--fc-weight-bold) var(--fc-size-rail-num) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-tight);
  color: var(--fc-text-ink);
}
.fc-where__col--weekend .fc-where__col-num { color: var(--fc-text-weekend-num); }
.fc-where__col--today   .fc-where__col-num { color: var(--fc-today-numeral); }
.fc-where__col--past    .fc-where__col-num { color: var(--fc-text-outside-num); }
.fc-where__col-dow {
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  /* design value: --fc-text-dow #AEB6C4 (2.04:1 on the card, 1.95:1 on today,
     1.74:1 on the weekend chip). Substituted for AA — this is the same column
     weekday label Month carries, and Month already made this exact
     substitution on .fc-dow__cell in css/calendar.css. Matching the design
     value here matched Month's MOCK, not Month's SHIPPED code. 5.78 / 5.53 /
     4.92:1 on the three grounds this element can sit on. */
  color: var(--fc-text-secondary);
}

/* ── the 104px person gutter ───────────────────────────────────────────── */
.fc-where__gutter {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 10px 0 12px;
  min-height: var(--fc-tap-desktop);
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background 160ms ease-out; /* TODO(motion-tokens) */
}
.fc-where__gutter--head { cursor: default; }
.fc-where__gutter:hover:not(.fc-where__gutter--head) { background: var(--fc-rail-hover); }
.fc-where__avatar {
  flex: 0 0 auto;
  width: var(--fc-w-avatar);
  height: var(--fc-w-avatar);
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--w-tile);
  color: var(--w-c);
  font: var(--fc-weight-bold) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
}
.fc-where__name {
  font: var(--fc-weight-semibold) var(--fc-w-size-name) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Selected person row — wash <colour>0d + a 3px left edge in <colour>, and
   the name in that colour (DESIGN-DECISIONS §1, UI-RULES §10). The edge is an
   inset shadow so it costs no layout and cannot shift the columns. */
.fc-where__lane.is-selected {
  background: var(--w-row-wash);
  box-shadow: inset 3px 0 0 0 var(--w-c);
}
.fc-where__lane.is-selected .fc-where__name {
  color: var(--w-c);
  font-weight: var(--fc-weight-bold);
}

/* ── one day cell ──────────────────────────────────────────────────────── */
.fc-where__cell {
  position: relative;
  z-index: 1;                       /* above the band, so clicks land here */
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-width: 0;
  min-height: var(--fc-w-lane-h);   /* 46px clears both the AA 24px floor and 44px */
  padding: 4px 6px;
  border: 0;
  border-left: 1px solid var(--fc-cell-vertical);
  background: transparent;
  cursor: pointer;
  font: inherit;
  transition: background 160ms ease-out; /* TODO(motion-tokens) */
}
/* ── the ground, in precedence order ───────────────────────────────────────
   ORDER IS LOAD-BEARING. The cell sits ABOVE its own band (z-index 1 vs 0), so
   any opaque ground on an away cell paints the band out. That shipped in the
   first pass: Cosmo away on a Saturday took the weekend ground and his band
   vanished for exactly that one day, leaving a hole in the middle of a
   three-day trip. Verified in Chrome at 1440px, 2026-08-06.

   So `--away` clears the ground and lets the band BE the ground, and `--travel`
   re-applies only the translucent <colour>1f wash, which reads as a halo in the
   10px of cell showing above and below the 26px band. All four selectors are a
   single class, so the later rule wins — do not reorder them. */
.fc-where__cell--home    { background: var(--fc-w-home-lane); }  /* home is a STATE WITH A COLOUR, not an absence (spec 08 §2) */
.fc-where__cell--weekend { background: var(--fc-w-weekend-lane); }
.fc-where__cell--today   { background: var(--fc-today-cell); }
.fc-where__cell--unknown { background: var(--fc-outside-cell); }
.fc-where__cell--away    { background: transparent; }
.fc-where__cell--travel  { background: var(--w-wash); }

/* A cell's left hairline draws the column rhythm — but over a band it chops one
   continuous pill into day-sized boxes, which is the exact "one box per day
   butting against the next" the band exists to avoid. Inside a run the rhythm
   is carried by the day numerals in the header instead. */
.fc-where__cell--away { border-left-color: transparent; }

/* Hover must not paint over the band either, so an away cell gets a ring rather
   than a ground change. */
.fc-where__cell:not(.fc-where__cell--away):hover { background: var(--fc-rail-hover); }
.fc-where__cell--away:hover { box-shadow: inset 0 0 0 2px var(--fc-surface-card); }

/* the location line — 13/700, in the person's colour on an away day */
.fc-where__loc {
  max-width: 100%;
  font: var(--fc-weight-semibold) var(--fc-w-size-loc) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* `Home` — 11/700/.04em #98A1AE. Muted, NOT faint: DESIGN-DECISIONS §6 records
   that #C3CAD8 at this size fails contrast on the lane ground.
   `:not(--away)` on the weekend arm is load-bearing. Without it this rule also
   caught an AWAY day that happens to fall on a Saturday: the later `--away`
   rule fixed the colour but nothing overrode the SIZE, so those two cells
   rendered their location line at 11px while the weekdays beside them were
   13px. Measured on 24–25 Oct — 11px vs 13px on the same trip (2026-08-06). */
.fc-where__cell--home .fc-where__loc,
.fc-where__cell--weekend:not(.fc-where__cell--away) .fc-where__loc {
  font-size: var(--fc-size-meta);
  letter-spacing: var(--fc-track-micro);
  color: var(--fc-text-muted-2);
}
/* an unrecorded day says so — it never borrows `Home` */
.fc-where__cell--unknown .fc-where__loc {
  font-size: var(--fc-size-meta);
  letter-spacing: var(--fc-track-micro);
  color: var(--fc-text-muted-3);
}

/* On the solid band the label is white — the house convention for a trip bar,
   and the one Oli explicitly accepted below AA for these hues
   (tests/contrast.check.js ACCEPTED_BELOW_AA, 2026-08-03).
   THIS INCLUDES THE TRAVEL DAY. The first pass gave a travel cell the person's
   colour, for a tapered band that left half the cell on the pale wash; with the
   band spanning whole cells at week scope that put orange text on an orange
   bar and `Travels to Madrid` disappeared completely. Verified in Chrome,
   2026-08-06. The travel day is still marked three ways — the wash halo above
   and below the band, the rounded end cap, and the word "Travels". */
.fc-where__cell--away .fc-where__loc { color: var(--fc-text-on-fill); }
/* Tentative is the exception: its band is a <colour>26 tint, not a solid, so
   the label takes the person's colour and the copy carries a trailing ' ?' —
   the design's three simultaneous signals (DESIGN-DECISIONS §4). */
.fc-where__cell--tentative .fc-where__loc { color: var(--w-c); }

/* the generated sub-note — `day 3 of 7 · Schengen`, 11/500 */
.fc-where__note {
  max-width: 100%;
  font: var(--fc-weight-regular) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Full white, NOT 86%. A white label on Oli's orange is already the knowingly
   accepted 2.92:1 (tests/contrast.check.js); dimming it to 86% pushes the
   sub-note below even that, for decoration. The size difference already
   separates the note from the location line. */
.fc-where__cell--away .fc-where__note { color: var(--fc-text-on-fill); }
/* …except on a tentative tint, where the ground is pale and the note takes ink */
.fc-where__cell--tentative .fc-where__note { color: var(--fc-text-secondary); opacity: 1; }
/* On an OVERLAP day the sub-note is not a day count — it is the other PLACE
   the person is (`+ Travels to London`), which is the same class of fact as the
   location line above it and the only place that trip is named in words. So it
   takes the medium weight rather than the regular one: still smaller than the
   13px location line, so the hierarchy holds, but no longer reading as the
   throwaway metadata `day 3 of 7` is. */
.fc-where__cell--overlap .fc-where__note { font-weight: var(--fc-weight-medium); }

/* ── the away band ─────────────────────────────────────────────────────────
   POSITIONED against the lane, BEHIND the cells: one continuous shape rather
   than one box per day butting against the next. `pointer-events:none` so the
   cells above keep every click.

   The geometry is stated exactly rather than left to a percentage margin on a
   grid item — that resolved against the whole row rather than the item's grid
   area and put the band two columns adrift (Chrome, 1440px, 2026-08-06). The
   columns occupy `100% - <origin>`, where origin is the gutter on desktop and
   0 on the mobile strip; one column is that over `--b-cols`. js/views/where.js
   supplies the three numbers. */
.fc-where__band {
  position: absolute;
  /* The default is ONE band filling the whole envelope, centred in the lane —
     the geometry every non-overlapping day has always had. It was written as
     `top:50%` + `translateY(-50%)`; it is a static offset either way, and one
     way of expressing a position is enough now that the two overlap treatments
     below have to move it. `--b-lane` / `--b-lanes` come from
     js/views/where.js, which gets them from FamCalEngine.assignLanes. */
  --b-lane: 0;
  --b-lanes: 1;
  --fc-w-band-env:  var(--fc-w-band-h);
  --fc-w-band-slot: var(--fc-w-band-env);
  top: calc(50% - var(--fc-w-band-env) / 2);
  left: calc(var(--fc-w-band-origin) +
             (100% - var(--fc-w-band-origin)) * var(--b-start) / var(--b-cols));
  right: calc((100% - var(--fc-w-band-origin)) * var(--b-end-inv) / var(--b-cols));
  z-index: 0;
  height: var(--fc-w-band-slot);
  /* the radius follows the slot, so a 12px pill is 6px, not a 13px clip */
  border-radius: min(var(--fc-w-radius-band), calc(var(--fc-w-band-slot) / 2));
  background: var(--w-c);
  pointer-events: none;
  /* centres the label the bar carries wherever the cells carry no words */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 8px;
}
/* ── OVERLAP, treatment 1: SPLIT — bare scopes only ─────────────────────────
   Month scope and mobile put no words in the cell, so there is nothing for a
   divided envelope to cut through. The 26px (22px on mobile) envelope divides
   evenly: two 12px pills 2px apart, each still carrying its own trip name, and
   the pair occupies exactly the space one band did — so the halo above and
   below is untouched. js/views/where.js adds the class only when the cells are
   bare AND the lane actually needed more than one sub-lane.

   THE LABEL DOES NOT SCALE WITH THE PILL, and nothing in this rule can make it:
   the band is `overflow: visible` and `.fc-where__band-label` carries a fixed
   11px line box (9.5px on mobile). Measured at 1440px: two trips give a 12px
   pill against an 11px label and it fits; three give 7.33px, and 3.67px of
   label would hang out of its own pill into the neighbour's with no clipping to
   stop it. So `fitBandLabels()` in js/views/where.js measures the line box
   against the pill after layout and drops the label when it no longer fits —
   the same answer, and the same FONTS §4 rule ("no label at all rather than a
   broken one"), as the horizontal test that already lives there. Verified with
   a real third trip injected into `domain.trips` and `personDays`: at two trips
   the labels read `Morocco` / `London`; at three, all three are dropped and the
   pills carry no text. The trips are still named in the cell's accessible name
   at every scope. */
.fc-where__band--split {
  --fc-w-band-slot: calc((var(--fc-w-band-env) - (var(--b-lanes) - 1) * var(--fc-w-band-gap))
                         / var(--b-lanes));
  top: calc(50% - var(--fc-w-band-env) / 2
            + var(--b-lane) * (var(--fc-w-band-slot) + var(--fc-w-band-gap)));
}

/* ── OVERLAP AT WEEK SCOPE: NO SECOND BAND TREATMENT, DELIBERATELY ──────────
   The split above is not available here — the cell carries `Travels to
   Morocco` in white ON the band, and the 2px gap cuts its descenders (the
   arithmetic is on the --split rule). So every run keeps the FULL envelope.
   All the bands in one lane are that person's single colour, so two
   overlapping runs merge into one continuous away region, which is exactly
   what is true: she is away all of those days.

   A SUB-STRIPE WAS BUILT FOR THIS CASE AND HAS BEEN REMOVED (2026-08-06,
   convergence pass). `.fc-where__band--sub::after` drew the second trip's own
   days as a 6px bar in the 10px of halo below the band. It was geometrically
   sound — it divided the halo, so it degraded to nothing rather than
   overflowing the lane — and it still failed on the only test that counts.
   Two independent reads of the render agreed: 6px of the same pink, 2px under
   a 26px band, aria-hidden and unlabelled, running exactly beneath the
   sub-line copy. It read as a text-decoration rule under that copy, or as a
   doubled band edge; nothing said "this is a second trip, and these are its
   days". Stacked with the band it took 34px of a 47px lane.

   Nothing replaces it, and nothing needs to. The COPY carries both the fact
   and the span and always did: the cell reads `Morocco +1` with
   `+ Travels to London` beneath it, and the accessible name states every trip
   with its own direction and day count. Removing the graphic removes a mark
   that decoded as noise, not a fact.

   Mobile had reached the better answer in its own file: bare cells mean the
   split IS available there, so the same day renders two labelled pills plus
   `away · Morocco 17–20 · London 18–20`. That answer stands; this scope
   simply has no graphic to add to the words.

   Reopen only with the constraint that produced the stripe: a fixed 46px lane
   plus white copy sitting on the band. Loosen either — a second lane row for
   an overlap, or ink-on-tint copy — and better options exist. */

/* The band is 26px in a 46px lane, so 10px of the cell's own ground shows above
   and below it. That is what makes the travel day's <colour>1f wash visible as
   a halo around the bar — the same relationship desktop-where.png shows, and
   the reason the taper can be dropped at week scope without losing the signal. */
.fc-where__grid  { --fc-w-band-origin: var(--fc-w-gutter); }
.fc-where__strip { --fc-w-band-origin: 0px; }
.fc-where__band--cont-left {
  border-top-left-radius: var(--fc-radius-barcut);
  border-bottom-left-radius: var(--fc-radius-barcut);
}
.fc-where__band--cont-right {
  border-top-right-radius: var(--fc-radius-barcut);
  border-bottom-right-radius: var(--fc-radius-barcut);
}
.fc-where__band--tentative {
  background: var(--w-tentative);
  border: 1.4px dashed var(--w-dashed);
}
/* The bar names itself wherever the cells carry no words (month scope, and
   mobile) — otherwise colour and lane position would be the only thing telling
   Oli's Madrid from Laura's Bristol, and colour is never the sole carrier.
   FONTS.md gives no Where band-label row, so this takes the estate's bar-label
   size; mobile drops to 9.5/700 per FONTS §3 "Mobile". */
.fc-where__band-label {
  font: var(--fc-weight-medium) var(--fc-size-bar-label) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-on-fill);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fc-where__band--tentative .fc-where__band-label { color: var(--w-c); }

/* ── the legend ────────────────────────────────────────────────────────── */
.fc-where__legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 20px;
  padding: 12px 4px 0;
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-secondary);
}
.fc-where__legend-item { display: inline-flex; align-items: center; gap: 8px; }
/* The swatches DEPICT the treatments rather than standing in for them — three
   identical pale pills would explain nothing, which is what the first pass
   shipped. They are drawn in the shared navy (--fc-person-shared, the "two or
   more of us" hue) because the legend is about the SHAPE of a treatment, not
   about whose lane it is. */
.fc-where__swatch {
  width: 30px;
  height: 18px;
  display: block;
  border-radius: 9px;
  background: var(--fc-person-shared);
}
/* the halo: the band with the person's wash showing above and below it */
.fc-where__swatch--travel {
  height: 18px;
  border-top: 4px solid var(--fc-weekend-chip);
  border-bottom: 4px solid var(--fc-weekend-chip);
  background-clip: padding-box;
}
.fc-where__swatch--home {
  background: var(--fc-w-home-lane);
  border: 1px solid var(--fc-divider);
}


/* ════════════════════════════════════════════════════════════════════════
   SECTION 4 — THE SCHENGEN HALF, IN ITS UNAVAILABLE STATE

   Neutral ground, never the green panel: green is the colour of a computed,
   comfortable count (DESIGN-DECISIONS §1, "green is reserved for Schengen"),
   and there is no count. Same reading as the shell's `EU —/90` header chip.
   ════════════════════════════════════════════════════════════════════════ */
.fc-where__eu { display: flex; flex-direction: column; gap: 14px; }

.fc-where__eu-head { display: flex; align-items: baseline; gap: 12px; }
.fc-where__eu-title {
  font: var(--fc-weight-bold) var(--fc-size-section) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
  margin: 0;
}
.fc-where__eu-sub {
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted-3);
}
.fc-where__eu-rule { flex: 1 1 auto; height: 1px; background: var(--fc-hairline-card); }

.fc-where__eu-body {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) 2fr;
  gap: 14px;
  align-items: stretch;
}

.fc-where__eu-figure,
.fc-where__eu-trips {
  padding: 18px 20px;
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
.is-unavailable .fc-where__eu-figure { background: var(--fc-neutral-chip); }

/* --fc-text-secondary, not --fc-text-muted. FONTS §3 fixes the size and weight
   of a micro-caps label but names no colour for it, so this is the view's call
   — and measured on the panel ground, muted #8A94A6 comes out at 2.78:1, under
   the 4.5:1 a 10px label needs. Secondary #5C6679 measures 5.25:1 on the same
   ground and is still unmistakably a quiet label (2026-08-06). */
.fc-where__eu-caps {
  display: block;
  font: var(--fc-weight-bold) var(--fc-w-size-caps) / var(--fc-leading-flat) var(--fc-font);
  letter-spacing: var(--fc-track-caps);
  color: var(--fc-text-secondary);
}
.fc-where__eu-count { display: flex; align-items: baseline; gap: 8px; margin: 12px 0 10px; }
.fc-where__eu-big {
  font: var(--fc-weight-bold) var(--fc-w-size-count) / var(--fc-w-leading-count) var(--fc-font);
  letter-spacing: var(--fc-w-track-count);
  /* The em-dash of an ABSENT figure is grey, never the green of a real reading
     — the reader must not have to squint to tell them apart. Secondary rather
     than muted-2: at 46px the AA bar is 3:1 for large text, and muted-2 on the
     neutral panel measured 2.37:1, under even that. Secondary is 5.25:1 and
     still reads as "no number here" beside the green a real count would use. */
  color: var(--fc-text-secondary);
}
.fc-where__eu-of {
  font: var(--fc-weight-semibold) var(--fc-w-size-of) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted);
}
.fc-where__eu-track {
  height: 8px;
  border-radius: 4px;
  background: var(--fc-skeleton-bar);   /* an EMPTY track: a fill length is a figure */
}
.fc-where__eu-reason {
  margin: 10px 0 0;
  max-width: 62ch;
  font: var(--fc-weight-medium) var(--fc-size-qualifier) / var(--fc-leading-body) var(--fc-font);
  color: var(--fc-text-secondary);
  text-wrap: pretty;
}

.fc-where__eu-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 2px; }
.fc-where__eu-item {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 7px 4px;
  border-top: 1px solid var(--fc-hairline-row);
}
.fc-where__eu-item:first-child { border-top: 0; }
/* The day chip is the TRIP'S OWN LENGTH (derive.js `spanOf`), not a Schengen
   sum — so it takes the neutral chip, never the green one, and cannot be
   misread as a count. */
.fc-where__eu-days {
  justify-self: start;
  padding: 3px 7px;
  border-radius: var(--fc-radius-badge);
  background: var(--fc-neutral-chip);
  color: var(--fc-text-secondary);
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
}
.fc-where__eu-name {
  font: var(--fc-weight-semibold) var(--fc-size-body) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fc-where__eu-when {
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted);
}
.fc-where__eu-more {
  display: inline-block;
  margin-top: 8px;
  font: var(--fc-weight-semibold) var(--fc-size-chip-line2) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted);
}

/* ── the rolling-window frame ──────────────────────────────────────────── */
.fc-where__eu-window {
  padding: 14px 20px 16px;
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
.fc-where__eu-window-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.fc-where__eu-window-note {
  font: var(--fc-weight-medium) var(--fc-size-meta) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted-3);
}
.fc-where__eu-axis {
  display: grid;
  grid-template-columns: repeat(13, minmax(0, 1fr));
  margin-top: 10px;
  height: 56px;
  border-radius: var(--fc-radius-chip);
  background: var(--fc-skeleton-cell);
  border: 1px solid var(--fc-hairline-card);
}
.fc-where__eu-tick {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 6px;
  border-left: 1px solid var(--fc-cell-vertical);
  min-width: 0;
}
.fc-where__eu-tick:first-child { border-left: 0; }
/* today is a calendar fact, so it is the one mark the frame may carry */
.fc-where__eu-tick.is-now { box-shadow: inset 2px 0 0 0 var(--fc-today); }
.fc-where__eu-tick-label {
  font: var(--fc-weight-semibold) var(--fc-size-badge) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-dow);
  overflow: hidden;
}


/* ════════════════════════════════════════════════════════════════════════
   SECTION 5 — EMPTY / ERROR: NOTHING HERE, DELIBERATELY

   This file used to own the whole component: `.fc-where__overlay`,
   `.fc-where__state`, its glyph tile, title, body, actions and two buttons.
   It was the CORRECT one — DESIGN-DECISIONS §6 specifies one shared pattern
   for the estate and this lane was the only one that built it — while Year,
   Day and the shell's not-built placeholder drew a left-aligned dashed panel
   instead.

   PROMOTED 2026-08-06 into calendar.css SECTION 5, unchanged, as `.fc-state`
   + `.fc-state-layer`. Its two remaining private values went with it:
   --fc-w-overlay is now the brand token --fc-state-overlay, --fc-w-state-shadow
   is --fc-shadow-state, and --fc-w-glyph-error resolved to the
   --fc-breach-ground that already existed. Nothing is redeclared here — a
   local alias for a component this file no longer owns is exactly how two
   copies come back.
   ════════════════════════════════════════════════════════════════════════ */


/* ════════════════════════════════════════════════════════════════════════
   SECTION 6 — LOADING SKELETONS
   No spinner (DESIGN-DECISIONS §6). Skeletons sit at the real geometry so
   nothing jumps when the data lands, and the Schengen figure is masked as a
   grey pill rather than showing a `0`.

   WHAT WAS WRONG, 2026-08-07. `render()` has always set `data-state` and this
   section has always existed, so the DESKTOP board masked correctly. It was
   only ever HALF the screen. The mask was written against the desktop lane
   classes, and the mobile board is a different set of elements
   (`__card-sum`, `__day`, `__day-num`) that no rule here reached — so at 390px
   a `LOADING…` pill sat over three fully legible cards reading
   "away · Morocco 17–20" in each person's own colour, with the tinted day
   grounds and every numeral. The Schengen trip list leaked at both widths, and
   so did the desktop day numerals the reference export masks
   (exports/loading-where.png). Measured at 1440×900 and 390×844.

   The rule the mask follows: anything DERIVED FROM THE DATA is masked; the
   chrome §6 keeps in every state — weekday letters, the date range, the
   legend, the standing explanations — is not. Geometry never changes, so
   nothing moves when the real data lands.
   ════════════════════════════════════════════════════════════════════════ */

/* ── grounds: every data cell drops to the flat skeleton ground ─────────── */
.fc-where[data-state='loading'] .fc-where__cell { background: var(--fc-skeleton-cell); }
/* Mobile day cells, INCLUDING the tinted away / weekend / today grounds — a
   person's wash on a loading day is the trip colour, which is data. The
   today ring goes with it; today is redrawn when the data lands. */
.fc-where[data-state='loading'] .fc-where__day {
  background: var(--fc-skeleton-cell);
  box-shadow: none;
}
.fc-where[data-state='loading'] .fc-where__band { background: var(--fc-skeleton-bar); }
.fc-where[data-state='loading'] .fc-where__avatar { background: var(--fc-skeleton-bar); color: transparent; }

/* ── text placeholders: 8px tall, radius 4 (§6) ─────────────────────────── */
.fc-where[data-state='loading'] .fc-where__loc,
.fc-where[data-state='loading'] .fc-where__note,
.fc-where[data-state='loading'] .fc-where__name,
.fc-where[data-state='loading'] .fc-where__col-num,
.fc-where[data-state='loading'] .fc-where__card-sum,
.fc-where[data-state='loading'] .fc-where__day-num,
.fc-where[data-state='loading'] .fc-where__eu-name,
.fc-where[data-state='loading'] .fc-where__eu-when,
.fc-where[data-state='loading'] .fc-where__eu-days,
.fc-where[data-state='loading'] .fc-where__eu-more {
  color: transparent;
  background: var(--fc-skeleton-text);
  border-radius: 4px;
  min-height: 8px;
}
/* The band is already a grey bar at real bar geometry; its label only needs to
   stop being readable, not to grow a second pill inside the first. */
.fc-where[data-state='loading'] .fc-where__band-label { color: transparent; }
.fc-where[data-state='loading'] .fc-where__eu-big {
  color: transparent;
  background: var(--fc-skeleton-bar);
  border-radius: var(--fc-radius-badge);
}


/* ════════════════════════════════════════════════════════════════════════
   SECTION 7 — NARROW DESKTOP WIDTHS
   Between the phone breakpoint and the design's 1320px the lanes have to give
   somewhere. The gutter shrinks before the day columns do, because the day
   columns are the answer and the gutter is the label.
   ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1180px) {
  .fc-where { --fc-w-gutter: 84px; }
  .fc-where__eu-body { grid-template-columns: 1fr; }
  .fc-where__loc { font-size: var(--fc-size-meta); }
}
@media (max-width: 860px) {
  .fc-where { --fc-w-gutter: 64px; }
  /* Below this a location line cannot be read in a seventh of the width, so
     the words drop out and the band plus the accessible name carry the day —
     the same rule month scope follows, and FONTS §4's "no label at all rather
     than an ellipsis of one character". */
  .fc-where__loc, .fc-where__note { display: none; }
  .fc-where__name { font-size: var(--fc-size-meta); }
}
/* Month scope packs 28–31 columns into the same template — the numerals stay,
   the words are already gone (dropped in js/views/where.js, not hidden here,
   so they never reach the DOM to be read out twice). */
.fc-where[data-scope='month'] .fc-where__col-num { font-size: var(--fc-size-chip); }


/* ════════════════════════════════════════════════════════════════════════
   SECTION 8 — MOBILE (≤ 430px): one card per person, no RangeSwitch
   The tab bar is the shell's; this file only owns what is inside the view.
   ════════════════════════════════════════════════════════════════════════ */
.fc-where--mobile { gap: 16px; }
.fc-where--mobile .fc-where__controls { flex-wrap: wrap; }
/* --fc-tap is 44px — FONTS §4 / ruling D3, the estate's tap rule since the
   2026-08-09 rename. --fc-tap-desktop (24) is the fine-pointer exception and
   never applies at this width. */
.fc-where--mobile .fc-where__step-btn {
  min-height: var(--fc-tap);
  min-width: var(--fc-tap);
  padding: 10px 16px;
  font-size: var(--fc-size-qualifier);
}
.fc-where--mobile .fc-where__range-label { font-size: var(--fc-size-meta); }

.fc-where__board--stack { display: flex; flex-direction: column; gap: 12px; }

.fc-where__card {
  padding: 12px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-radius-panel);
  background: var(--fc-surface-rail);
}
.fc-where__card.is-selected {
  background: var(--w-row-wash);
  box-shadow: inset 3px 0 0 0 var(--w-c);
}
.fc-where__card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.fc-where__card-head .fc-where__name { font-size: var(--fc-size-body); }
/* the one-line answer, in the person's colour (FONTS §3: location line 13/700) */
.fc-where__card-sum {
  margin-left: auto;
  font: var(--fc-weight-semibold) var(--fc-w-size-loc) / var(--fc-leading-flat) var(--fc-font);
  color: var(--w-c);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `gap: 0` is load-bearing: the band's calc() divides the strip's width into
   exactly --b-cols equal columns, and a grid gap would make each column
   narrower than that share, so the band would drift further right the further
   into the week it starts. The 4px of air between day cells is their own
   margin instead, which sits inside the column and cannot skew the maths. */
.fc-where__strip {
  display: grid;
  grid-template-columns: repeat(var(--fc-w-cols), minmax(0, 1fr));
  gap: 0;
  position: relative;
}
.fc-where__day {
  grid-row: 1;
  z-index: 1;
  margin: 0 2px;
  min-height: var(--fc-tap);   /* 44px — ruling D3 */
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 6px 2px;
  border: 1px solid var(--fc-hairline-card);
  border-radius: var(--fc-m-radius-cell);
  background: var(--fc-surface-card);
  cursor: pointer;
}
.fc-where__day--weekend { background: var(--fc-w-weekend-lane); }
.fc-where__day--today   { background: var(--fc-m-today-cell); box-shadow: inset 0 0 0 1.5px var(--fc-m-today-ring); }
.fc-where__day--unknown { background: var(--fc-outside-cell); }
/* Every away day takes the wash, as mobile-where.png shows — the whole run is
   tinted and the bar rides over it. Declared last so it beats the weekend and
   today grounds, which would otherwise break the run into tinted and untinted
   days for no reason the reader can see. */
.fc-where__day--away    { background: var(--w-wash); }
.fc-where__day-num {
  font: var(--fc-weight-semibold) var(--fc-m-size-day-num) / var(--fc-leading-flat) var(--fc-font);
  color: var(--fc-text-muted-2);
}
.fc-where__day--away .fc-where__day-num { color: var(--fc-text-secondary); }
/* On mobile the band sits UNDER the numerals rather than centred, so the
   numeral stays legible at the top of the cell — as mobile-where.png shows.
   Because the two never overlap, the band goes ABOVE the cells here (z-index 2)
   rather than behind them: that keeps the bar one unbroken shape across the run
   while each day still shows its own tinted, rounded cell. Behind them, an
   opaque day ground chopped the bar into per-day chunks — verified at 390px,
   2026-08-06. `pointer-events:none` is what keeps every tap on the day cell. */
.fc-where--mobile .fc-where__band {
  /* Same split, bottom-anchored: the envelope is the 22px bar rather than the
     26px lane band, and sub-lane 0 stacks UPWARD so it is the top bar here as
     it is on desktop. `transform` is not reset because the desktop rule no
     longer sets one. */
  --fc-w-band-env: var(--fc-w-band-h-mobile);
  top: auto;
  bottom: calc(var(--fc-w-band-bottom) +
               (var(--b-lanes) - 1 - var(--b-lane)) *
               (var(--fc-w-band-slot) + var(--fc-w-band-gap)));
  z-index: 2;
  /* 4px, not 8: at 8px a two-day bar leaves a 32px content box and `Bristol`
     needs 32.4px, so a 0.4px overflow ellipsed it to `Bris…` and cost three
     characters. Measured at 390px, 2026-08-06. */
  padding: 0 4px;
}
/* FONTS §3 Mobile: the bar label drops to 9.5/700 — the one place weight goes
   UP as size goes down, because mobile bars are narrower and shorter. */
.fc-where--mobile .fc-where__band-label {
  font-size: var(--fc-m-size-bar-label);
  font-weight: var(--fc-weight-semibold);
}
.fc-where--mobile .fc-where__eu-body { grid-template-columns: 1fr; }
.fc-where--mobile .fc-where__eu-axis { height: 44px; }
.fc-where--mobile .fc-where__eu-big { font-size: 34px; }   /* FONTS §3: 34–46 */


/* ════════════════════════════════════════════════════════════════════════
   SECTION 9 — REDUCED MOTION
   calendar.css SECTION 6 carries the estate's block, but it cannot know about
   selectors that did not exist when it was written (contract §5). Every
   transition this file adds degrades to instant here. Nothing in this view
   hijacks scroll, animates layout, or moves anything on the page — the only
   effects are ground and border colour changes on hover — so there is no
   travel to disable beyond this.
   ════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .fc-where__range-btn,
  .fc-where__step-btn,
  .fc-where__gutter,
  .fc-where__cell {
    transition: none;
  }
  /* `.fc-where__btn` used to be listed here. Its component moved to
     calendar.css SECTION 5 as `.fc-state__btn`, and that file's reduced-motion
     block already carries a universal `transition-duration: .01ms !important`
     which covers it. Nothing is lost by dropping the name; leaving a rule for
     a class this file no longer emits is dead CSS. */
}
