/* Shell — column: fixed menu bar (64px) sits above scrolling content.
   Pages that scroll internally should give their content a top offset of
   64px (the bar is position:fixed and does not take layout space). */
/* --- The frame — app-wide maximum-width clamp -------------------------
   The whole app lives in one centred frame. Past --frame the sides are
   the base ground (black), so a Studio Display gets a centred site, not
   a stretched one. Every full-bleed device (bands, code grounds, split
   modes, hairlines) clips at the frame edge: "full-bleed" means the
   width of the frame, and the frame is the page. */
/* Frame and rail: /shell/tokens.css (--frame, --rail). */
.shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  max-width: var(--frame);
  margin: 0 auto;
  position: relative;
  background: transparent;
  overflow: visible;
  /* No z-index. A stacking context on this full-page column is a compositor
     plate over #shellField even while background is transparent (live, every
     browser, William 2026-08-27). The field is z-index 0 with pointer-events
     none, never -1, never mix-blend. No overflow-x clip here either. Do not
     move clip onto html or body: height 100% plus overflow-x clip forces
     overflow-y clip, which eats the footer. Rails use min-width 0 on
     .surface. The menu bar (#topChrome) must NOT be a child of this node. */
  /* Two side rails mark the frame's max-width edge, top to bottom on every
     page — the one hairline the menu line wears (--line), so the frame reads as
     a held column, not a loose centred block (William, 2026-08-10). */
  border-inline: 1px solid var(--line);
}

/* --- Menu bar (fixed top chrome) ----------------------------- */
/* Fixed, but clamped to the same frame as the page — the glyph and
   omnibox align with the content column; beyond the frame, black. */
.top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: var(--frame);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  height: 64px;
  padding: 0;
  /* Transparent at the top of the page, solid once anything scrolls under.
     Because --bar-surface IS --g, this is pixel identical everywhere except
     over a full-bleed hero, where the chrome floats on the picture and the
     hero's own scrim carries legibility. is-scrolled already exists for the
     hairline; the ground rides the same state. Open menu always sits on
     solid ground. */
  background: transparent;
  transition: background-color 200ms ease;
  border-bottom: 0;
  z-index: 30;
  overflow-x: clip;
}
.top.is-scrolled,
.top.is-open {
  background: var(--bar-surface);
}
/* The bar's opaque ground reaches 1px past its base (a ground-coloured shadow),
   so the fixed bottom line rides over SOLID ground where it crosses the two side
   rails: without it the rail's --line and the bar line's --line (both the same
   translucent hairline) stack at the cross and read brighter (William, 2026-08-10). */
.top.is-scrolled { box-shadow: 0 1px 0 var(--bar-surface); }
/* The bar's bottom hairline is the one line that IGNORES the frame clamp —
   it runs the full window width (fixed, so ancestor clips can't cut it),
   while the bar content stays aligned to the frame. It is absent at the
   very top of the page and fades in once the content scrolls under the bar
   (is-scrolled set by surfaces-nav.js), so the bar floats free on the hero
   and only earns its edge once there is something to divide. */
.top::after {
  content: "";
  position: fixed;
  top: 64px;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
}
.top.is-scrolled::after { opacity: 1; }
/* overflow-x:clip forces overflow-y clip — lift both when menu is open */
.top.is-open {
  overflow: visible;
}
/* Menu continues the chrome; list carries the bottom stroke */
.top.is-open::after { display: none; }
.top-bar {
  position: relative;
  /* Far left = product weather · far right = search. */
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  height: 64px;
  min-height: 64px;
  /* Left inset IS the rail (glyph, menu rows and content all begin here);
     right inset is the chrome inset the search/hamburger sit in (--menu-x-r). */
  padding: 0 16px 0 var(--rail);
  gap: 14px;
  background: transparent; /* wash comes from .top */
}
.top-bar > .top-left { justify-self: start; min-width: 0; }
.top-bar > .top-right { justify-self: end; }
/* Open: no hairline under the bar — list is continuous chrome */
.top.is-open .top-bar {
  border-bottom: 0;
}
.top-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 0 0 auto;
  height: 100%;
}
.top-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
/* Language switch — dead-centre of the bar, independent of the left breadcrumb
   and right hamburger widths (the bar is position:relative). Bracketed codes in
   terminal ink; brackets are dim pseudo-elements so the code reads first. The
   lit one is the current language (EN at rest). Styling only — wiring to real
   translation lands later. */
.lang-switch {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 1;
  /* Shown state: the switch waits for the wordmark behind it to clear before it
     fades back in (see .lang-chosen below), so the two never overlap mid-swap. */
  transition: opacity 300ms ease 200ms;
}
.lang-opt {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  min-width: var(--hit);
  min-height: var(--hit);
  padding: 4px 2px;
  border: 0;
  background: transparent;
  font-family: "Fira Code", ui-monospace, monospace;
  font-weight: 500;
  font-size: var(--t-lede);
  letter-spacing: var(--track-micro);
  line-height: 1;
  /* Brightness is carried by opacity on the ink, not a lighter token, so the
     whole row sits well below the bar chrome and still flips for light theme. */
  color: var(--ink);
  opacity: 0.62;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 140ms ease;
}
/* Brackets always render in the mono face at the button's size, so the three
   bracket-pairs align cleanly even though the JP code is a CJK glyph. They sit a
   touch dimmer than the code they wrap. */
.lang-opt::before,
.lang-opt::after {
  font-family: "Fira Code", ui-monospace, monospace;
  letter-spacing: 0;
  opacity: 0.6;
}
.lang-opt::before { content: "["; margin-right: 2px; }
.lang-opt::after  { content: "]"; margin-left: 2px; }
.lang-opt:hover,
.lang-opt:focus-visible { opacity: 1; outline: none; }
/* Current language: brighter than the resting codes, still muted (never full white). */
.lang-opt.is-on { opacity: 0.85; }
/* Narrow bars: keep the codes off the breadcrumb by tightening the row. */
@media (max-width: 560px) {
  .lang-switch { gap: 8px; }
  .lang-opt { font-size: var(--t-small); }
}
/* The bar's centre — the live Nicoya (Costa Rica) clock. A DSEG7 Modern LCD
   readout (the machine voice), seconds and all, centred on the bar's midline the
   same way the glyph and the hamburger are (absolute + translate), so all three
   share one line. The language switch that used to sit here now lives in the
   footer. mountBarClock() (nav.js) feeds the digits; the colons blink in CSS. */
.bar-clock {
  /* Centred by LAYOUT, not a transform: span the whole bar (inset 0) and let the
     grid place the digits dead centre. A transform would put the composited layer
     on a subpixel row, and WebKit/WKWebView re-rounds that row when the menu
     toggles the bar's overflow — the 1px up/down jump seen only in the app. With
     no transform the layer sits on the pixel grid and holds still. It is a
     readout, so pointer-events:none lets clicks reach the glyph and hamburger. */
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  /* The digits share the cloud's baseline (--bar-baseline). Bottom-align the
     cell, then subtract the gap from text-box bottom to digit ink: 0.16em,
     settled by pixel measurement at 17px and 19px (line-height 1.3 puts
     0.15em of space below the digits; the ink reaches a hair past its
     metric baseline). Em units track the clamp() font size. Layout only —
     a transform would jump 1px in WKWebView (see the centring note above). */
  place-items: end center;
  padding-bottom: calc(var(--bar-baseline) - 0.16em);
  pointer-events: none;
  white-space: nowrap;
  /* Matches the Tamara/Asia nameplate face on /scent — the DSEG14 Classic Mini
     LCD (--font-lcd), not the old 7-segment Modern (William, 2026-08-06). */
  font-family: var(--font-lcd);
  font-style: normal;
  font-weight: 400;
  font-synthesis: none;
  /* Mobile sits at 17px and reads well; the desktop cap is held close to it so
     the seconds-readout never balloons on wide bars (24px was too big).
     Scaled 22% smaller then 11% back up — net × 0.8658 of the old 17/4vw/19
     (William, 2026-08-06). */
  font-size: clamp(14.72px, 3.46vw, 16.45px);
  letter-spacing: var(--track-micro); /* the nameplate tracking (William, 2026-08-06) */
  /* Headroom so the DSEG glyph ink is never clipped by the bar's overflow, top
     or bottom — the face sits tall in its em box. */
  line-height: 1.3;
  color: var(--ink);
  opacity: 0.82; /* the nameplate's receded chrome brightness (William, 2026-08-06) */
  /* The brushed-silver chrome bevel lifted from /scent's .pcard-name — a crisp
     top gleam + a hard dark crease that reads as polished metal against the bar
     ground, matching the Tamara/Asia nameplates (William, 2026-08-06). */
  text-shadow:
    0 -0.5px 0 rgba(239, 239, 239, 0.55),   /* top gleam */
    0 0.6px 0 rgba(16, 16, 16, 1);          /* crisp dark crease */
  -webkit-font-smoothing: antialiased;
}
.bar-clock-face { grid-area: 1 / 1; } /* the live digits */
/* The unlit-segment ghost — DSEG's signature. "88:88:88" is the all-segments
   state; stacked in the same grid cell so it centres by layout too (no transform)
   and sits pixel-aligned behind the lit digits, in the off-hardware tone. */
.bar-clock::before {
  content: "88:88:88";
  grid-area: 1 / 1;
  z-index: -1;
  /* The SAME off tone the weather mark's dormant segments wear — --wx-off
     (the one OFF hardware tone, #101010), single-sourced from the unified
     light layer, so the clock's off segments read exactly as the glyph's
     (William, 2026-08-06). */
  color: var(--wx-off);
  /* The lit digits' chrome text-shadow is an inherited property, so the ghost
     picked up the bright top gleam and read too hot. Off segments carry no
     bevel — kill it here so they sit dark behind the digits (William, 2026-08-06). */
  text-shadow: none;
  letter-spacing: inherit;
  pointer-events: none;
}
/* Angel numbers — three or more of the same digit in a row (colons aside,
   the way 21:11:13 reads "111") shine a touch brighter WHITE, never a colour:
   the fill lifts to the clock's brightness ceiling, the top gleam brightens,
   and a soft white bloom makes the run glow, so it reads as a brighter run of
   the same LCD hardware rather than a loud hue (William, 2026-08-08; was the
   page-primary colour). mountBarClock (nav.js) toggles the class. */
.bar-clock .bw-d.is-angel {
  color: var(--lit);
  filter: brightness(1.15);
  text-shadow:
    0 -0.5px 0 rgba(239, 239, 239, 0.75),   /* brighter top gleam */
    0 0.6px 0 rgba(16, 16, 16, 1),          /* the dark crease, unchanged */
    0 0 4px rgba(239, 239, 239, 0.5);       /* the soft white shine */
}
/* The clock holds dead still through open and close — same size, same place,
   same brightness — so the bar centre never jumps when the menu takes over. */
/* The colons blink like a clock; the digits hold steady. */
.bar-clock .bw-cln { animation: bwBlink 1s steps(1, end) infinite; }
@keyframes bwBlink { 50% { opacity: 0.2; } }
@media (max-width: 560px) {
  .bar-clock { font-size: 14.72px; } /* 17px × 0.8658 — tracks the shrink + 11% nudge (William, 2026-08-06) */
}
@media (prefers-reduced-motion: reduce) {
  .bar-clock .bw-cln { animation: none; }
}
/* The hamburger — two lines that fold into an X when the menu is open.
   Sole open/close control. */
.menu-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  min-width: 46px;
  height: 46px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--r);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  flex-shrink: 0;
  transition: color 140ms ease;
}
.menu-toggle:hover,
.menu-toggle:focus-visible { color: var(--ink); outline: none; }
.ham-bars { position: relative; display: block; width: 22px; height: 12px; }
.ham-bar {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 200ms ease, opacity 200ms ease;
}
.ham-bar:nth-child(1) { top: 2px; }
.ham-bar:nth-child(2) { bottom: 2px; }
/* Open → the two lines cross into a centred X.
   The bar uses .top.is-open; a host that only mounts the button (no #topChrome)
   uses aria-expanded, the state the toggle already owns. */
.top.is-open .ham-bar:nth-child(1),
.menu-toggle[aria-expanded="true"] .ham-bar:nth-child(1) { transform: translateY(3.5px) rotate(45deg); }
.top.is-open .ham-bar:nth-child(2),
.menu-toggle[aria-expanded="true"] .ham-bar:nth-child(2) { transform: translateY(-3.5px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .ham-bar { transition: none; } }

.brand-wrap {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  align-self: center;
  /* No nudge: the mark is tight-cropped (menu-weather-mark.svg), so its left edge sits on
     the rail by structure, not by a compensating offset. */
}
/* Permanent surface icon — larger than menu glyphs */
.brand-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  /* Glyph hugs the button's left edge, which sits on the rail (top-bar inset),
     so the mark's left lands on --rail with no offset. The touch target keeps
     its 56px via min-width; the slack falls to the glyph's right. */
  justify-content: flex-start;
  background: transparent;
  border: 0;
  border-radius: var(--r);
  margin: 0;
  cursor: pointer;
  line-height: 0;
  width: auto;
  height: 56px;
  min-width: 56px;
  min-height: 56px;
  padding: 0;
  opacity: 1;
  transition: opacity 160ms ease, filter 160ms ease;
}
/* Permanent surface icon is ALWAYS lit — full colour and animating at rest.
   The unlit/hover-lit law governs the menu-list glyphs below. Hover lives on
   the glyph itself — never a box */
.brand-btn:hover,
.brand-btn:focus-visible { background: transparent; outline: none; filter: brightness(1.15); }
.brand-btn:active { filter: brightness(1); }
/* Open: the top-left mark stays at full brightness — it is the live preview of
   the hovered row's glyph, so it must read clearly. */
.top.is-open .brand-btn {
  opacity: 1;
  background: transparent;
}
.top.is-open .brand-btn:hover {
  opacity: 1;
  background: transparent;
}
.brand-btn .brand-mark {
  display: block;
  overflow: hidden;
  /* ONE weather component: the bar wears the square all-segments master at
     --wx-glyph, the SAME size as the footer status mark and on-page weather
     marks. The file never changes. data-wx-light lights the relevant
     segments; LIGHT_CROP grows that cell to fill this box. */
  width: var(--wx-glyph, 52px);
  height: var(--wx-glyph, 52px);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
/* Cold-load flash guard: the glyph mounts as a raw <img>
   of the COLOUR master, then inlineGlyph swaps it for the recoloured, surface-
   lit <svg>. Hold that raw <img> invisible so its baked colours never flash on
   a hard refresh — only the <img> is hit (the inline <svg> isn't), and a failed
   inline re-reveals it (.is-fallback) so the mark can never vanish. */
.brand-btn img.brand-mark { opacity: 0; }
.brand-btn img.brand-mark.is-fallback { opacity: 1; }

/* Menu-bar title — path (e.g. /studio-archive/season-one/colours) on every surface but home.
   Flex row: each path segment is preceded by the hard-rain drop streak as its
   separator. Alignment is handled by align-items:center — no magic baselines. */
.brand-word {
  display: inline-flex;
  align-items: center;
  margin: 0;
  font-family: "Fira Code", ui-monospace, monospace;
  font-weight: 500;
  font-size: var(--t-label);
  letter-spacing: 0;
  text-transform: none; /* keep path casing exact */
  color: var(--ink);
  line-height: 1;
  white-space: nowrap;
  user-select: none;
  -webkit-user-select: none;
  flex-shrink: 0;
  /* Optical middle with weather mark */
  transform: translateY(0.5px);
}
/* Leading identity wordmark — the mark's companion in the menu-bar lockup.
   Sits in .top-left after the glyph; the route reads at the label size. */
.brand-lead {
  /* Holds the FULL route breadcrumb. Capped so it leaves room for the
     top-right search magnifier and can't run away on wide screens. */
  flex: 0 1 auto;
  min-width: 0;
  max-width: min(64vw, 44ch);
  overflow: hidden;
}
.top.is-open .brand-word { opacity: 1; }

