/* ============================================================
   fab2 — site v4 · "the reticle"
   The site is one lithographic mask. There is no document
   scroll: a fixed stage hosts the mask canvas + a transformed
   world layer of DOM stops. The header is the only navigation.
   Brand rules carried from v2: one grey (#928585), no black,
   six titanium colors as discrete blocks, Vipnagorgialla for
   display, iridescence never sorted into a rainbow.
   ============================================================ */

@font-face {
  font-family: 'Vipnagorgialla';
  src: url('assets/vipnagorgialla.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

:root {
  --paper:  #ffffff;
  --ink:    #928585;
  --ink-body: #5f5656; /* non-black text token for dense readable copy */
  --ink-2:  #a79d9d;   /* decoration only — fails text contrast */
  --ink-3:  #bbb3b3;   /* decoration only */
  --line:   #ece8e7;
  --line-2: #dad3d2;
  --sunken: #f6f4f3;
  --chrome-bg: rgba(255, 255, 255, 0.96);
  --chrome-edge: rgba(146, 133, 133, 0.22);
  --surface-shadow: 0 16px 36px -30px rgba(95, 86, 86, 0.44);

  --pink:     #eb85a3;
  --gold:     #eebf00;
  --green:    #94c47d;
  --sky:      #80b7d7;
  --lavender: #c699c3;
  --taupe:    #928585;

  --font-display: 'Vipnagorgialla', 'Arial Black', sans-serif;
  --font-body:    'Hanken Grotesk', system-ui, sans-serif;
  --font-mono:    'Fragment Mono', ui-monospace, Menlo, monospace;

  --header-h: 52px;
  --sky-h: 14px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

*, *::before, *::after { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  height: 100%;
  overflow: hidden;          /* the camera scrolls, the page never does */
  overscroll-behavior: none;
  /* hacky iOS bar-hide: the document is 1px taller than the viewport (see the
     html override below) so a downward flick gives Safari something to scroll
     and it minimizes the toolbar. Everything visible is position:fixed, so the
     1px of travel moves nothing; overscroll:none stops the rubber-band.
     ponytail: 1px scroll trick; if iOS ignores it, only home-screen mode hides it */
  background: var(--sunken);
  color: var(--ink);
  font-family: var(--font-body);
  /* CRITICAL: containers render at tiny CSS zoom when the camera is far
     out; iOS text autosizing would inflate those fonts back up and
     shred the layout (giant words in a sliver-wide column) */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  isolation: isolate;         /* keep difference-blended cues on the scene backdrop */
}
/* makes the document scrollable by exactly 1px (bar-hide trick above) */
html { overflow-x: hidden; overflow-y: auto; min-height: calc(100% + 1px); }

/* ---------------- stage ---------------- */
#stage {
  position: fixed; inset: 0;
  touch-action: none;
  cursor: crosshair;
}
#mask,
#glass {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
#glass {
  pointer-events: none;
}
/* plain anchor at the stage origin — the engine positions each child
   in screen space per frame (no scaled mega-layer: it would rasterize
   once and GPU-stretch, blurring everything on iOS) */
#world {
  position: absolute; left: 0; top: 0;
  width: 0; height: 0;
  visibility: hidden;            /* revealed once the engine has placed
                                    everything — no unstyled flash */
}
body.booted #world { visibility: visible; }

/* ---------------- placed containers (world-space DOM) ----------------
   position/rotation/scale come from data-x/y/rot/scale via engine.js;
   CSS only sets each container's own box */
.plate-item {
  display: block;
  pointer-events: none;      /* decoration — clicks fall through to the plate */
  user-select: none;
}
/* content plots — frosted glass panes in the fab2 palette (structure
   only for now). 500×600 CSS box, data-w 1000 ⇒ engine scales ×2. */
.cbox {
  width: 500px;
  height: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--lavender) 26%, #fbfaf9);
  border: 1px solid var(--line-2);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.92),
    0 10px 24px rgba(146, 133, 133, 0.14);
  user-select: none;
}
.cbox b {
  font: 700 4rem/1 var(--font-display);
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
  text-transform: lowercase;
  letter-spacing: 0.01em;
}
/* a photo fills its pane edge to edge, cropped to the portrait frame */
.cbox img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* the studio µ-print's mark: a small faded standard-cell stamp printed
   just below the die's copy — palette inks, discrete, not a backdrop */
.studio-stamp {
  display: block;
  width: 600px;
  height: 220px;
  opacity: 0.4;
  pointer-events: none;
  user-select: none;
}
.studio-stamp svg {
  display: block;
  width: 100%;
  height: 100%;
}
/* ---------------- the mobile print (scattered phone dies) ----------------
   Six 420×880 CSS dies dropped into the wafer's free pockets at their
   own rotations and sizes (world size = data-w, see index.html). No
   frame, no backing — the ink prints straight on the glass like every
   other page (the gds fabric densifier already clears around ink). */
.mobile-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 420px;
  padding: 34px 30px 30px;
  margin: 0;
  border: 0;
  background: transparent;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
}
/* the panel root swallows pointer events; real links inside must not */
.mobile-panel a {
  pointer-events: auto;
}
.mobile-panel h2 {
  margin: 0 0 18px;
  font: 700 46px/0.94 var(--font-display);
  color: var(--ink);
  text-transform: lowercase;
  letter-spacing: 0.01em;
}
.mobile-panel p {
  margin: 0;
  max-width: none;
  font: 500 17px/1.5 var(--font-body);
  color: var(--ink-body);
}
.mobile-panel p + p {
  margin-top: 14px;
}
.mobile-panel img {
  display: block;
  width: 100%;
  height: 250px;
  object-fit: cover;
  margin: 0 0 18px;
  opacity: 0.92;
}
/* HOME die: the litany is the hero, the blurb rides under it */
.mobile-prints-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
}
.mobile-prints-copy span {
  max-width: 100%;
  font: 700 27px/1.1 var(--font-display);
  color: var(--ink);
  text-transform: lowercase;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
/* p.mp-blurb: must outrank `.mobile-panel p`'s font shorthand, or the
   size silently stays 17px */
.mobile-panel p.mp-blurb {
  margin-top: 20px;             /* tight under the litany — dies hug content */
  font-family: 'Baloo 2', var(--font-body);
  font-size: 15px;              /* "…design and print any chip" holds one line */
  line-height: 1.5;
}
/* three claims, three lines — like the desktop original's <br/> breaks.
   nowrap + the measure-and-fit script (index.html) guarantee ONE line
   per claim on any device's font metrics */
.mp-blurb span {
  display: block;
  white-space: nowrap;
}
.mp-blurb span + span {
  margin-top: 8px;
}
.mp-founders {
  font-weight: 600;
}
/* one size down so "possible." and "Keller." pull up into their rows */
.mobile-panel-about p {
  font-size: 16px;
}
/* ABOUT die: the follow-us link — live, like the careers chip */
.mp-socials {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: 100%;
  margin-top: 22px;
}
.mp-x {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 0;
  color: var(--ink);
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  pointer-events: auto;
}
.mp-yt {
  margin-top: 0;
}
/* SITES die: the globe floats in the die's lower half inside a relative
   wrap that carries the OG map tags + leaders at inline % positions */
.mp-globe-wrap {
  position: relative;
  width: 100%;
  max-width: 320px;
  margin: 20px auto 0;
  align-self: center;
}
.mobile-panel img.mp-globe {
  height: auto;
  object-fit: contain;
  margin: 0;
  opacity: 1;
}
/* the OG tags are cut for a 520px globe; this one is ~320px — scale ~62% */
.mp-globe-wrap .gtag {
  font-size: 0.46rem;
  padding: 0.32rem 0.45rem;
}
/* SITES die: a compact scale read — ATX dominates, SF/FF sit below */
.mobile-site-scale {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
}
.site-scale-row {
  display: grid;
  grid-template-columns: 5fr 6fr;
  gap: 10px;
}
.site-scale-card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 0;
  padding: 12px;
  background: color-mix(in srgb, var(--site-c, var(--ink-2)) 18%, var(--paper));
  border: 2px solid var(--site-c, var(--ink-2));
  color: var(--ink-body);
}
.site-scale-atx {
  position: relative;
  min-height: 104px;
  padding-right: 108px;
}
.site-scale-sf,
.site-scale-ff {
  min-height: 82px;
}
.site-scale-card b {
  display: block;
  font: 700 25px/0.9 var(--font-display);
  color: var(--site-c, var(--pink));
  text-transform: lowercase;
  white-space: nowrap;
}
.site-scale-card strong {
  display: block;
  margin-top: auto;
  font: 700 38px/0.86 var(--font-display);
  color: var(--site-c, var(--pink));
  letter-spacing: 0;
  white-space: nowrap;
}
.site-scale-card strong em {
  font: 400 10px/1 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.08em;
  vertical-align: baseline;
}
.site-scale-card small {
  display: block;
  margin-top: 6px;
  font: 400 10px/1.25 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-body);
}
.site-scale-row .site-scale-card {
  padding: 10px;
}
.site-scale-row .site-scale-card b {
  font-size: 20px;
}
.site-scale-row .site-scale-card strong {
  font-size: 27px;
}
.mobile-panel-sites .mp-globe-wrap {
  width: 90%;
  max-width: none;
  margin-top: 12px;
}
.mp-hq-star {
  position: absolute;
  top: 14px;
  right: 14px;
  display: block;
  width: 78px;
  height: 78px;
}
.mp-hq-star svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.mp-hq-star path {
  fill: var(--pink);
  stroke: var(--pink);
  stroke-width: 4px;
  stroke-linejoin: round;
}
.mp-hq-star text {
  fill: var(--paper);
  stroke: color-mix(in srgb, var(--pink) 58%, var(--paper));
  stroke-width: 1.8px;
  paint-order: stroke fill;
  font: 700 38px/1 var(--font-display);
  letter-spacing: 0;
}
/* CAREERS die: live count from Ashby (empty until the fetch lands).
   Header + counter run tight — the card grid needs the vertical room */
.mobile-panel-careers h2 {
  margin-bottom: 6px;
}
.mp-role-stats {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-top: 0;
}
.mp-role-stats:empty { display: none; }
.mp-role-count {
  font: 700 64px/0.9 var(--font-display);
  color: var(--ink);
}
.mp-role-lbl {
  margin-top: 6px;
  font: 400 12px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
}
/* CAREERS die: four square team cards under the CTA. Category view
   (engineering/manufacturing/operations/interns) flips into that team's
   three most-recent roles + a back card — filled by careers.js. */
.mp-cards {
  display: grid;
  /* minmax(0,…): a plain 1fr's min-size is its content, and the nowrap
     chips would blow the tracks out past the die's edge */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 12px;
  width: 100%;
  margin-top: 18px;
  pointer-events: auto;
}
.mp-cards:empty { display: none; }
/* cards get a team-color fill layer at 75% opacity.
   HARD square: fixed height (= the 174px track the 420px die yields),
   overflow hidden — a long role title may never stretch the card */
.mp-card {
  position: relative;
  height: 174px;
  background: transparent;
  border: 2px solid var(--c, var(--taupe));
  margin: 0;
  padding: 14px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  text-align: left;
  overflow: hidden;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.mp-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--c, var(--taupe));
  opacity: 0.75;
  pointer-events: none;
}
.mp-card > * {
  position: relative;
  z-index: 1;
}
.mp-card-head {
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.mp-card b {
  font: 700 15px/1.35 var(--font-mono);
  color: var(--paper);
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* .mp-card-site sits OUTSIDE the head — space-between pins it to the
   card's bottom edge (role cards: title top, site bottom, no chip) */
.mp-card-head em,
.mp-card-site {
  font: 400 11px/1.3 var(--font-mono);
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  opacity: 0.9;
}
.mp-card-go {
  align-self: stretch;         /* spans the locked square edge to edge */
  text-align: center;
  padding: 10px 4px;
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.55);
  color: var(--paper);
  font: 700 10px/1 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}
/* role titles are arbitrary copy — a step smaller than the category
   labels so long titles hold inside the locked square */
.mp-card-role b {
  font-size: 12px;
  line-height: 1.45;
  -webkit-line-clamp: 5;
  hyphens: auto;
  -webkit-hyphens: auto;
}
/* empty lots: the grid always shows four boxes — dashed, muted, inert */
.mp-card-empty {
  border-style: dashed;
  align-items: center;
  justify-content: center;
  cursor: default;
  opacity: 0.6;
}
.mp-card-empty b {
  font-size: 12px;
}
/* the roles view's pager: back / next, a short row under the squares */
.mp-card-btn {
  background: rgba(255, 255, 255, 0.4);
  border: 2px solid var(--c, var(--taupe));
  color: var(--c, var(--ink));
  margin: 0;
  padding: 15px 10px;
  font: 700 13px/1 var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* CAREERS die: the one live control on the strip — a real link out */
.mobile-careers-chip {
  display: block;
  width: 100%;               /* spans the card grid below it */
  margin-top: 18px;
  padding: 15px 22px;
  background: rgba(255, 255, 255, 0.4);
  border: 1px solid var(--green);
  color: var(--green);
  font: 700 15px/1 var(--font-mono);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-decoration: none;
  pointer-events: auto;
}
/* (the slide-show booster plots retired — the ride now pans across the
   slime-mold transistor field baked onto the plate itself) */
.stop {
  cursor: auto;              /* stops are pages, not plate */
  width: 1280px;
  height: 840px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.6rem;
  padding: 3.5rem 4rem;
}
/* Let stops paint normally during flights. Forcing compositor layers here
   makes WebKit/Chromium tile-raster large transformed pages; at shallow
   angles the About image/text can appear as horizontal strips mid-pan. */
body.cam-fly .stop { will-change: auto; }
/* careers: 70% frosted-glass panel over the wafer plate behind it */
.stop[data-stop="people"] {
  position: relative;
  background: rgba(255, 255, 255, 0.96);   /* solid panel — no frost, so an
                                              ancestor blur can't blank it */
  border: 1px solid color-mix(in srgb, var(--line-2) 74%, #fff);
  border-radius: 8px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.92),
    var(--surface-shadow);
  z-index: 5;                /* Careers shares the SITES die center; lift it
                               above so its links/scroll get the clicks */
  justify-content: flex-start;  /* title + filters pin to the top; the list
                                   below takes the rest and scrolls */
  padding: 1.6rem 2rem;      /* tight — let the content claim the space */
  gap: 0;
  overflow: visible;
}
.careers-banner {
  position: absolute;
  inset: 0 0 auto;
  display: block;
  width: 100%;
  height: 13.25rem;
  object-fit: cover;
  object-position: center bottom;
  opacity: 0.4;
  border-bottom: 2px solid color-mix(in srgb, var(--line-2) 74%, #fff);
  pointer-events: none;
  z-index: 0;
}
.stop[data-stop="people"] .careers-head,
.stop[data-stop="people"] .role-filters,
.stop[data-stop="people"] .roles {
  position: relative;
  z-index: 1;
}
/* careers head: the big lowercase title with the live count riding its
   baseline — the ord's CAREERS/dot are hidden, the h2 already says it */
.stop[data-stop="people"] .careers-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: 1.2rem;
  row-gap: 0.45rem;
}
.stop[data-stop="people"] .careers-head h2 {
  grid-column: 1;
  grid-row: 1;
  font-size: 3.8rem;
}
.stop[data-stop="people"] #careers-ord {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  display: none;
}
#careers-ord .ord-lead,
#careers-ord .ord-mag { display: none; }
.stop[data-stop="people"] .ord-meta { font-size: 1.05rem; }
.stop[data-stop="people"] .role-chip { font-size: 0.98rem; padding: 0.58rem 0.86rem; }
.stop[data-stop="people"] .roles a { font-size: 1.22rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* live Ashby list can be long — it's the only scrolling region, sized to
   the space under the filters, one clean column (data-interactive lets
   the wheel scroll it instead of scrubbing the tour) */
.stop[data-stop="people"] .roles {
  position: absolute;
  left: 2rem;
  right: 1rem;
  top: 13.25rem;
  bottom: 0;
  flex: none;
  margin-top: 0;
  background: rgba(255, 255, 255, 0.96);
  min-height: 0;             /* let the flex child actually shrink + scroll */
  max-width: none;           /* fill the panel */
  overflow-y: scroll;
  scrollbar-gutter: stable;
  touch-action: pan-y;       /* re-enable touch scroll inside the touch-action:none stage */
  scrollbar-width: auto;
  scrollbar-color: var(--stop-color, var(--ink)) transparent;
}
.stop[data-stop="people"] .roles::-webkit-scrollbar { width: 16px; }
.stop[data-stop="people"] .roles::-webkit-scrollbar-track { background: transparent; }
.stop[data-stop="people"] .roles::-webkit-scrollbar-thumb {
  background: var(--stop-color, var(--ink));
  border-radius: 8px;
}
.roles-empty,
.roles-status {
  padding: 1rem 0.25rem;
  color: var(--taupe);
  font-style: italic;
}

/* careers intro — lead copy between the heading and the list */
.careers-lead {
  margin: 0.6rem 0 0.9rem;
  max-width: 44rem;
}
.stop .ord {
  font: 400 1.02rem/1 var(--font-mono);
  letter-spacing: 0.24em;
  color: var(--ink);
}
.ord-lead {
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--stop-color, var(--ink));
}
.stop .ord::before {
  content: '';
  display: inline-block;
  width: 0.85em; height: 0.85em;
  margin-right: 0.7em;
  vertical-align: -0.08em;
  background: var(--stop-color, var(--taupe));
}
.stop h2 {
  margin: 0;
  font: 700 6.2rem/0.95 var(--font-display);
  letter-spacing: 0.01em;
  color: var(--ink);
  text-transform: lowercase;
}
.stop p {
  margin: 0;
  max-width: 40rem;
  font-size: 1.32rem;
  line-height: 1.55;
  color: var(--ink-body);
}
/* the home litany — every permutation, full title size */
/* home: keep the printed copy tight to the etched frame so the section's
   corner marks and landing zoom don't include a dead band under the CTA. */
.stop-home { justify-content: flex-start; padding: 0.75rem 1rem; z-index: 4; }
.home-inner {
  flex: 1;
  min-height: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0;
  --home-pad-x: 0.6rem;
  --home-pad-y: 0.55rem;
  --home-copy-measure: 53rem;     /* wide enough that each Fab2 line stays whole */
}
.prints-list {
  position: absolute;
  left: var(--home-pad-x);
  right: var(--home-pad-x);
  top: 0.55rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.prints-list span {
  font: 700 5.38rem/0.895 var(--font-display);
  color: var(--ink);
  text-transform: lowercase;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.prints-list b:not([style]) { color: color-mix(in srgb, var(--ink) 84%, var(--paper)); }
.stop-home .home-blurb {
  position: absolute;
  top: auto;
  bottom: var(--home-pad-y);
  left: var(--home-pad-x);
  right: auto;
  width: var(--home-copy-measure);
  max-width: none;
  margin: 0;
  font-family: 'Baloo 2', var(--font-mono);   /* rounded voice for the
                                      blurb — the litany keeps the only
                                      display voice on this print */
  font-size: 1.32rem;
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: 0.01em;
  color: var(--ink-body);
  text-align: left;
  text-wrap: pretty;
}
.stop-home .home-blurb b { font-weight: 700; }
.tone-fab2 { color: var(--lavender); }
.tone-fab { color: var(--green); }
.tone-studio { color: var(--gold); }

/* stat strip — oversized notation, factory-scale numbers */
.stats {
  display: flex;
  gap: 3.5rem;
  margin-top: 0.5rem;
}
.stats div { display: flex; flex-direction: column; gap: 0.5rem; }
.stats b {
  font: 700 3.4rem/1 var(--font-display);
  color: var(--ink);
}
.stats span {
  font: 400 0.78rem/1.3 var(--font-mono);
  letter-spacing: 0.18em;
  color: var(--ink);
}

/* site blocks — full-saturation palette cards, white type */
.sites-row {
  display: flex;
  gap: 1.25rem;
  margin-top: 0.5rem;
}
.site-cell {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.sites-row .site-card {
  flex: none;                    /* cards size to their label — no wraps */
  padding: 1.2rem 1.2rem;
  background: var(--site-c, var(--taupe));
}
.sites-row b {
  display: block;
  font: 700 2rem/1 var(--font-display);
  color: #fff;
  margin-bottom: 0.45rem;
}
.sites-row span {
  font: 400 0.72rem/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  white-space: nowrap;
  color: #fff;
}

/* the headline runs FLAT on one line, sitting where its third line used
   to, and its right edge lands on the left edge of the SF square
   (592px from the column edge, measured against the live row) */
.stop[data-stop="sites"] h2 {
  width: fit-content;
  font-size: 72px;
  line-height: 0.88;
}
/* ---- site floorplates — true area ratios, side ∝ √(square footage).
   SF (25K sqft) is the baseline, Lockhart is 30K, Austin is 120K. ---- */
.sites-row { align-items: flex-end; gap: 1.45rem; }
.site-plate {
  position: relative;
  --site-side: calc(sqrt(var(--area-scale, 1)) * 150px);
  --site-w: var(--plate-w, var(--site-side));
  --site-h: var(--plate-h, var(--site-side));
  width: var(--site-w);
  height: var(--site-h);
  background: var(--site-c, var(--taupe));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* no clip: the word is SIZED to fit, and the HQ badge rides the corner */
  overflow: visible;
}
/* the code sits inside its plate — sized off the plate side ÷ glyph
   count, at ~88% of the wall-to-wall width */
.site-plate b {
  position: relative;
  font: 700 calc(var(--site-h) * 0.88 / (var(--chars, 4) * 0.62)) / 1 var(--font-display);
  color: #fff;
  margin: 0;
  white-space: nowrap;
}
.site-facts {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
  min-height: 3.6rem;             /* equal caption blocks keep plate bases level */
}
.site-facts .site-location {
  font: 700 1.05rem/1.1 var(--font-display);
  color: var(--site-c, var(--ink));
  margin: 0;
  white-space: nowrap;
}
.site-facts .site-kind {
  font: 700 1.05rem/1.1 var(--font-mono);
  color: var(--site-c, var(--ink));
  margin: 0;
  white-space: nowrap;
}
.site-facts .site-area {
  font: 400 0.66rem/1.5 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--ink-body);
  white-space: nowrap;
}
.grow-note { font-style: normal; font-weight: 700; color: var(--site-c); }

/* ---- regular web-page furniture inside stops ---- */
.stop a {
  color: var(--ink);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--stop-color, var(--ink-3));
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
  transition: background 160ms var(--ease-out);
}
.stop a:hover { background: var(--sunken); }
.stop code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--sunken);
  padding: 0.05em 0.35em;
}
.btn {
  appearance: none;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  padding: 0.8rem 1.4rem;
  font: 600 0.78rem/1 var(--font-mono);
  letter-spacing: 0.18em;
  cursor: pointer;
  transition: background 160ms var(--ease-out), color 160ms var(--ease-out);
}
.btn:hover { background: var(--ink); color: var(--paper); }

.intern-toggle {
  appearance: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  padding: 0;
  white-space: nowrap;
  text-transform: none;
}
.intern-toggle * { text-transform: none; }
.intern-label {
  color: currentColor;
  font: 700 1.235rem/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: none;
}
.intern-switch {
  position: relative;
  flex: none;
  width: 3.055rem;
  height: 1.586rem;
  border-radius: 999px;
  background: var(--line-2);
  transition: background 140ms var(--ease-out);
}
.intern-knob {
  position: absolute;
  top: 50%;
  left: 0.208rem;
  width: 1.17rem;
  height: 1.17rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(77, 68, 68, 0.22);
  transform: translate(0, -50%);
  transition: transform 170ms var(--ease-out);
}
.intern-toggle:hover {
  color: var(--pink);
}
.intern-toggle[aria-checked="true"] {
  color: var(--pink);
}
.intern-toggle[aria-checked="true"] .intern-switch {
  background: var(--pink);
}
.intern-toggle[aria-checked="true"] .intern-knob {
  transform: translate(1.469rem, -50%);
}

/* careers CTA — Wii-channel pill, matched to the camera controls:
   glossy white convex body + drop shadow + top-bubble reflection, with a
   purple glossy accent orb exactly like #joynub. Spans the home stop's
   bottom and is meant to be the obvious thing to press. */
.cta-careers {
  position: absolute;               /* orb is positioned against this pill */
  left: calc(var(--home-pad-x) + var(--home-copy-measure) + 1.5rem);
  right: var(--home-pad-x);         /* runs out to the litany's own inset — the
                                       pill ends where "chambers" ends */
  top: auto;
  bottom: var(--home-pad-y);        /* shares the blurb's baseline… */
  height: calc(3 * 1.55 * 1.32rem); /* …and its three-line height */
  margin-top: 0;
  width: auto;
  max-width: none;
  align-self: auto;
  display: flex;
  align-items: center;
  justify-content: center;          /* label + orb sit together, centered */
  gap: 1.1rem;
  appearance: none;
  border: 1px solid rgba(146, 133, 133, 0.30);
  border-radius: 999px;             /* full pill */
  background: linear-gradient(180deg, #ffffff 0%, #f8f6f5 58%, #eeeae9 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -8px 14px rgba(146, 133, 133, 0.075),
    0 7px 18px rgba(146, 133, 133, 0.20);
  color: var(--ink);
  padding: 0.6rem 2.85rem 0.6rem 1.1rem;      /* right room for the absolute orb */
  font: 400 1.5rem/1 var(--font-mono);        /* sized up to carry the taller pill */
  letter-spacing: 0.12em;            /* match the nav chip kerning */
  cursor: pointer;
  transition: box-shadow 160ms var(--ease-out), transform 120ms var(--ease-out);
}
.cta-careers::before {              /* glossy top bubble, like #joy / #zoomrail */
  content: '';
  position: absolute;
  left: 3%; right: 3%; top: 8%;
  height: 40%;
  border-radius: 50%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0));
  pointer-events: none;
}
.cta-label { position: relative; z-index: 1; }
.cta-orb {                          /* glossy accent sphere — same recipe as #joynub,
                                       sits as a knob on the pill's right end */
  position: absolute; z-index: 2;
  top: 50%; right: 0.66rem;
  transform: translateY(-50%);
  display: grid; place-items: center;
  width: 2rem; height: 2rem;
  border-radius: 50%;
  color: #fff;
  font-size: 1rem; line-height: 1;
  background: radial-gradient(circle at 32% 26%,
    #ffffff 0%,
    color-mix(in srgb, var(--lavender) 38%, #fff) 22%,
    var(--lavender) 62%,
    color-mix(in srgb, var(--lavender) 72%, #4d4444) 100%);
  box-shadow:
    0 2px 6px rgba(77, 68, 68, 0.24),
    inset 0 1px 1px rgba(255, 255, 255, 0.75);
}
.cta-careers:hover {
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, 0.95),
    inset 0 -13px 24px rgba(146, 133, 133, 0.16),
    0 9px 24px rgba(146, 133, 133, 0.27);
}
.cta-careers:active {
  transform: translateY(2px);
  box-shadow:
    inset 0 3px 9px rgba(146, 133, 133, 0.30),
    0 3px 9px rgba(146, 133, 133, 0.22);
}
.roles {
  list-style: none;
  margin: 0; padding: 0;
  max-width: 34rem;
}
.roles li { border: 0; }
/* each role is ONE plain anchor to the posting — the whole row is the
   click target, nothing else to intercept */
.roles a {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(9.5rem, auto) auto;
  align-items: center;
  column-gap: 1rem;
  padding: 1.04rem 1rem 1.04rem 0.25rem;
  font-size: 1.18rem;
  text-decoration: none;
  font-weight: 600;
  color: var(--ink-body);
}
.roles a:hover { background: color-mix(in srgb, var(--green) 6%, var(--paper)); }
.role-main {
  display: inline-flex;
  align-items: baseline;
  gap: 0.55rem;
  min-width: 0;
}
.role-title {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.role-team {
  flex: none;
  color: var(--team-c, var(--ink));
  font: 700 0.82em/1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.role-star {
  flex: none;
  color: var(--ink-3);
  font-size: 0.72em;
}
.role-site {
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.55rem;
  min-width: 0;
}
.role-location {
  flex: none;
  min-width: 0;
  text-align: right;
  color: var(--ink);
  font: 700 0.82em/1 var(--font-mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.role-apply {
  justify-self: end;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 1.85rem;
  padding: 0.38rem 0.68rem 0.36rem;
  border: 1px solid var(--green);
  background: var(--green);
  color: #fff;
  font: 700 0.82em/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.roles a:hover .role-apply {
  background: color-mix(in srgb, var(--green) 84%, var(--ink));
  border-color: color-mix(in srgb, var(--green) 84%, var(--ink));
}

/* department / location filter chips (careers) — house style: mono caps,
   accent border, fills with the stop colour when active */
.role-filters {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 13.975rem;
  grid-template-rows: auto auto;
  gap: 0.95rem 1.1rem;
  align-items: stretch;
  margin: 0;
  padding-bottom: 1.35rem;
  border-bottom: 0;
}
.role-filters #dept-chips { grid-column: 1; grid-row: 1; }
.role-filters #loc-chips { grid-column: 1; grid-row: 2; }
.role-filters .intern-toggle {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  justify-self: center;
}
.chips { display: flex; flex-wrap: wrap; gap: 0.55rem; }
/* location chips differentiate from department chips with palette blue */
#loc-chips { --stop-color: var(--sky); }
.role-chip {
  appearance: none;
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: #fff;
  color: var(--ink);
  font: 700 0.95rem/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.62rem 0.9rem;
  white-space: nowrap;
  transition: background 140ms var(--ease-out), color 140ms var(--ease-out), border-color 140ms var(--ease-out);
}
.role-chip:hover { border-color: var(--stop-color, var(--ink)); }
.role-chip[aria-pressed="true"] {
  background: var(--stop-color, var(--ink));
  border-color: var(--stop-color, var(--ink));
  color: #fff;
}

/* ---- the locations globe (ported from v2 about), inside SITES ---- */
.sites-cols {
  position: relative;
  display: block;
  width: 960px;
  min-height: 610px;
}
.sites-copy {
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
  /* fill the leftover column EXACTLY (container − globe − gap): sized by
     intrinsic text width it would breathe across zoom rebases and shove
     the globe sideways a few px per step — a visible lateral jump when
     you're 20× deep in the dither */
  min-width: 0;
}
.globe-wrap {
  position: absolute;
  width: 385px;
  left: 475px;
  top: -58px;
  transform: none;
}
.globe-wrap img { display: block; width: 100%; height: auto; }
.gtag {
  position: absolute;
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.5rem 0.7rem;
  background: var(--paper);
  border: 1px solid var(--line-2);
  font: 400 0.74rem/1 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink);
  white-space: nowrap;
}
.gsq { width: 0.8em; height: 0.8em; background: var(--c, var(--taupe)); }
.gleader {
  position: absolute;
  width: 1px;
  background: var(--ink-3);
}

/* tile layers (giant-art mosaics): opacity is driven per-frame by the
   engine, keyed to ZOOM — the dissolve rides the scroll itself */
.tile-layer {
  opacity: 0;
  visibility: hidden;
}
.tile-layer.on { visibility: visible; }
.tile-layer.pixel img {
  image-rendering: pixelated;    /* dither dots stay hard squares at any
                                    zoom — never bilinear mush (per-image
                                    via data-tiles-pixel; photos stay smooth) */
}

/* ---- vitrine photo frame (v2 rule: plain glass, no effects) ---- */
.vitrine {
  margin: 0;
  align-self: flex-start;
  border: 1px solid var(--line-2);
  background: var(--paper);
  padding: 0.6rem;
}
.vitrine img { display: block; width: 640px; height: auto; }
.stop[data-stop="about"] {
  --about-media-w: 460px;
  --about-media-h: 332px;
  justify-content: flex-start;
  gap: 1rem;
  padding: 2.5rem 3rem;
}
.about-grid {
  display: grid;
  grid-template-columns: repeat(2, calc(var(--about-media-w) + 1.2rem + 2px));
  gap: 1.45rem 1.55rem;
  align-items: start;
}
.about-photo img {
  width: var(--about-media-w);
}
.stop[data-stop="about"] .about-ord {
  font: 700 1.7rem/1 var(--font-display);
  letter-spacing: 0;
  color: var(--pink);
}
.stop[data-stop="about"] .about-ord::before {
  content: none;
}
.about-team-copy {
  min-height: calc(var(--about-media-h) + 1.2rem + 2px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.05rem;
}
.about-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(var(--about-media-h) + 1.2rem + 2px);
  min-width: 0;
}
.stop[data-stop="about"] .about-lede {
  font: 500 1.6rem/1.14 var(--font-body);
  color: var(--ink);
  margin: 0;
}
.stop[data-stop="about"] .about-line {
  font: 500 1.28rem/1.2 var(--font-body);
  color: var(--ink);
  margin: 0;
}
.stop[data-stop="about"] .founder-link,
.mobile-panel .founder-link {
  color: var(--founder-c);
  text-decoration: none;
}
.stop[data-stop="about"] .founder-link:hover,
.mobile-panel .founder-link:hover {
  color: var(--pink);
}
.founder-link-sam {
  --founder-c: var(--lavender);
}
.founder-link-jim {
  --founder-c: var(--gold);
}
.about-topics {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.05rem;
  min-height: calc(var(--about-media-h) + 1.2rem + 2px);
}
.about-topic {
  max-width: 28rem;
}
.about-topic h3 {
  display: block;
  color: var(--ink);
  font: 700 1.6rem/1 var(--font-display);
  margin: 0 0 0.38rem;
  text-transform: lowercase;
}
.about-topic p {
  margin: 0;
  font: 500 1.15rem/1.3 var(--font-body);
  color: var(--ink);
}
.about-placeholder {
  position: relative;
  width: calc(var(--about-media-w) + 1.2rem + 2px);
  height: calc(var(--about-media-h) + 1.2rem + 2px);
  align-self: start;
  margin: 0;
  border: 1px solid var(--line-2);
  background: var(--paper);
  padding: 0.6rem;
  box-sizing: border-box;
  overflow: hidden;
}
.about-placeholder img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.9;
}
.team-line {
  max-width: 40rem;
}
.team-socials {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.2rem;
  width: 100%;
}
/* the social links under the team line — quiet ink, warms on hover */
.team-x {
  display: inline-flex;
  flex: 0 1 auto;
  align-items: center;
  gap: 0.7rem;
  align-self: flex-start;
  margin-top: 0;
  color: var(--ink);
  text-decoration: none;
  transition: color 140ms var(--ease-out);
}
.team-x:hover { color: var(--pink); }
.team-x svg {
  width: 28px;
  height: 28px;
}
.team-x-label {
  font: 700 1.02rem/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.team-yt {
  margin-top: 0;
}

/* ---------------- header / navigation ---------------- */
header {
  position: fixed; left: 0; right: 0;
  top: env(safe-area-inset-top, 0px);   /* clears the iOS status bar in fullscreen; 0 in-browser */
  height: var(--header-h);
  display: flex; align-items: center;
  gap: 2rem;
  padding: 0 1.5rem;
  /* near-opaque chrome; backdrop blur dropped — at 95% white the frost
     barely showed and it re-blurred the backdrop every composited frame */
  background: var(--chrome-bg);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 10px 24px -22px rgba(95, 86, 86, 0.36);
  z-index: 10;
}
.wordmark-img {
  height: 100%;                  /* full header height */
  width: auto;
  display: block;
  align-self: stretch;
  margin-left: -1.5rem;          /* bleed flush into the corner */
  user-select: none;
  -webkit-user-drag: none;
}
nav {
  display: flex; gap: 0.25rem; margin-left: auto;
  align-self: stretch;           /* reach the header's bottom edge so the
                                    journey line can sit right on the
                                    viewport boundary without clipping */
  align-items: stretch;          /* chip boxes run flush to the line — the
                                    active accent box touches the track */
  /* narrow screens: chips scroll sideways instead of clipping —
     the nav is data-interactive so the camera ignores gestures here */
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
nav::-webkit-scrollbar { display: none; }
.chip {
  flex: none;
  text-transform: lowercase;
  appearance: none; border: 0; background: none;
  display: flex; align-items: center; gap: 0.55em;
  padding: 0.55rem 0.8rem;
  font: 400 0.72rem/1 var(--font-mono);
  letter-spacing: 0.14em;
  color: var(--ink);
  cursor: pointer;
  transition: background 160ms var(--ease-out);
}
.chip:hover { background: var(--sunken); }
.chip .sq {
  width: 0.8em; height: 0.8em;
  border-radius: 50%;                 /* subway dots, not squares */
  border: 1px solid var(--ink-3);     /* unvisited: empty circle */
  /* fill tracks scrub proximity (engine drives --fill 0..1) */
  background: color-mix(in srgb, var(--c) calc(var(--fill, 0) * 100%), var(--paper));
  transition: border-color 200ms;
}
.chip.visited .sq, .chip.passed .sq { border-color: var(--c); }
/* the current station: hard white box with the accent OUTLINE — the
   outline, the riser and the journey line share the viewport's color,
   reading as one connected circuit */
.chip.active {
  background: var(--paper);
  box-shadow: inset 0 0 0 2px var(--c);
  border-radius: 8px 8px 0 0;    /* rounded shoulders; feet meet the line */
}
.chip.active .sq { border-color: var(--c); }

/* the subway line: a track through the chip squares; the fill is the
   train's position along the journey, stations light as it passes */
nav { position: relative; }
.nav-line {
  position: absolute;
  top: 50%;
  margin-top: -5px;            /* through the square centers (squares sit
                                  slightly high of center due to label
                                  baselines — measured in JS anyway) */
  height: 2px;
  background: var(--line-2);
  pointer-events: none;
  z-index: 0;
}
.nav-seg {                    /* one leg of the journey, in its own color */
  position: absolute;
  top: 0;
  height: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 140ms linear;
}
.nav-riser {                   /* arrival connector: the journey line
                                  turns UP into the dot through a rounded
                                  90° elbow (engine recolors per viewport) */
  position: absolute;
  width: 16px;
  border-right: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  border-bottom-right-radius: 8px;
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
  z-index: 2;
}
/* OFF THE RAILS: free input (drag / WASD / free zoom) drops the journey
   line off its track — it sinks and fades while the FREEROAM chip takes
   the active state. Chip clicks put you back on and the line rises. */
.nav-line {
  transition: opacity 280ms var(--ease-out), transform 340ms var(--ease-out);
}
header.derailed .nav-line { opacity: 0.15; transform: translateY(7px); }
header.derailed .nav-riser { opacity: 0; }
.chip { position: relative; z-index: 1; }
.chip .sq { background: var(--paper); }   /* stations punch through the track */
.chip.passed .sq { background: var(--c); }   /* border-color handled above */

/* hamburger toggle — desktop hidden, revealed on phones (see media query).
   the bars span draws ≡ via three lines; it becomes ✕ when nav-open */
/* floating "advance one viewport" button — a glossy pill in the current
   viewport's color (--acc, set by the engine like the joystick controls),
   same accent recipe as .cta-orb / #joynub. Pinned just above the bottom. */
#nextbtn {
  --acc: var(--taupe);              /* engine overrides per viewport */
  display: flex;
  position: fixed; left: 50%;
  bottom: calc(0.8rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  align-items: center; justify-content: center;
  padding: 0.7rem 2.6rem;
  appearance: none;
  border: 1px solid color-mix(in srgb, var(--acc) 52%, var(--chrome-edge));
  border-radius: 999px;             /* pill */
  background: radial-gradient(circle at 32% 26%,
    #ffffff 0%,
    color-mix(in srgb, var(--acc) 38%, #fff) 22%,
    var(--acc) 62%,
    color-mix(in srgb, var(--acc) 72%, #4d4444) 100%);
  box-shadow:
    0 2px 6px rgba(77, 68, 68, 0.20),
    inset 0 1px 1px rgba(255, 255, 255, 0.72);
  color: #fff;
  font: 400 1.6rem/1 var(--font-mono);
  cursor: pointer;
  z-index: 10;
  transition: background 350ms ease, border-color 350ms ease;
}
#nextbtn:active { filter: brightness(0.95); }
/* on phones the swipe IS the control — the pill just covered the dies */
body.is-mobile #nextbtn { display: none; }

/* the scroll/swipe hint is retired now that the next button carries the cue */
#hint { display: none; }

#fab2-cues {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9;
  mix-blend-mode: difference;
}
body.is-mobile #fab2-cues { display: none; }

.nav-toggle {
  display: none;             /* revealed on phones (see media query) */
  margin-left: auto;
  flex: none;
  height: 40px;
  align-items: center; justify-content: center;
  gap: 0.6em;
  padding: 0 0.2rem;
  appearance: none; border: 0; background: none;
  cursor: pointer;
  font: 400 0.78rem/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--ink);
}
.nav-toggle-lbl { display: inline-block; }
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: ""; display: block;
  width: 22px; height: 2px;
  background: var(--ink);
  transition: transform 220ms var(--ease-out), opacity 160ms var(--ease-out);
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before { position: absolute; top: -7px; left: 0; }
.nav-toggle-bars::after  { position: absolute; top:  7px; left: 0; }
header.nav-open .nav-toggle-bars { background: transparent; }   /* middle bar hides */
header.nav-open .nav-toggle-bars::before { transform: translateY(7px) rotate(45deg); }
header.nav-open .nav-toggle-bars::after  { transform: translateY(-7px) rotate(-45deg); }

/* the permanent footer: coordinate strip (wearing the current
   viewport's color, white text) */
#foot {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 10;
  pointer-events: none;
}
#hud {
  padding: 0.5rem 1.5rem;
  /* same chrome as the header — one material, blur dropped (see header) */
  background: var(--chrome-bg);
  border-top: 1px solid var(--line);
  color: var(--ink);
  font: 400 0.72rem/1 var(--font-mono);
  letter-spacing: 0.14em;
  user-select: none;
}

/* ---------------- desktop camera controls ---------------- */
#camctl {
  position: fixed;
  top: calc(var(--header-h) + 18px);   /* clears the chrome */
  right: calc(16px + 1.2rem);          /* inboard of the sky sliver */
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  user-select: none;
}
/* no desktop camera controls on touch — nor at phone widths, where the
   mobile strip tour runs and the joystick would sit on top of the dies */
body.is-mobile #camctl { display: none; }

/* glass material (Wii-channel feel): convex gradient body, soft drop
   shadow, and a bubble reflection across the top. Accents follow the
   current viewport's color via --acc (set by the engine). */
#camctl { --acc: var(--taupe); }
#joy, #zoomrail, #rot {
  position: relative;
  border: 1px solid var(--chrome-edge);
  background:
    linear-gradient(180deg, #ffffff 0%, #f4f1f0 55%, #ece8e7 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -6px 10px rgba(146, 133, 133, 0.075),
    0 3px 8px rgba(146, 133, 133, 0.18);
  cursor: grab;
  touch-action: none;
}
#joy:active, #rot:active { cursor: grabbing; }
/* the bubble: a glossy ellipse hugging the top of each control */
#joy::before, #zoomrail::before, #rot::before {
  content: '';
  position: absolute;
  left: 9%; right: 9%; top: 4%;
  height: 44%;
  border-radius: 50%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0));
  pointer-events: none;
  z-index: 2;
}

#joy {
  width: 76px; height: 76px;
  border-radius: 50%;
  /* crosshair + minimal direction arrows ride the body as one svg layer */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='76' height='76'%3E%3Cg stroke='%23dad3d2' stroke-width='1'%3E%3Cpath d='M38 16V60M16 38H60'/%3E%3C/g%3E%3Cg fill='%23a79d9d'%3E%3Cpath d='M38 6l3.5 5.5h-7z'/%3E%3Cpath d='M38 70l-3.5-5.5h7z'/%3E%3Cpath d='M6 38l5.5-3.5v7z'/%3E%3Cpath d='M70 38l-5.5 3.5v-7z'/%3E%3C/g%3E%3C/svg%3E") center / 76px 76px no-repeat,
    linear-gradient(180deg, #ffffff 0%, #f4f1f0 55%, #ece8e7 100%);
}
#joynub {
  position: absolute;
  left: 50%; top: 50%;
  width: 28px; height: 28px;
  border-radius: 50%;
  /* glossy accent sphere */
  background: radial-gradient(circle at 32% 26%,
    #ffffff 0%,
    color-mix(in srgb, var(--acc) 38%, #fff) 22%,
    var(--acc) 62%,
    color-mix(in srgb, var(--acc) 72%, #4d4444) 100%);
  box-shadow:
    0 2px 5px rgba(77, 68, 68, 0.22),
    inset 0 1px 1px rgba(255, 255, 255, 0.75);
  transform: translate(-50%, -50%);
  transition: background 350ms ease;
  z-index: 3;                /* topmost — never clips under the gloss */
}

#zoomrail {
  width: 34px; height: 210px;
  border-radius: 17px;
  cursor: pointer;
  /* the track runs between the 0% and 100% thumb points */
  background:
    linear-gradient(var(--line-2), var(--line-2)) center / 1px calc(100% - 64px) no-repeat,
    linear-gradient(180deg, #ffffff 0%, #f4f1f0 55%, #ece8e7 100%);
}
#zoomrail::before {                    /* tall pill gets a slim top bubble */
  height: 16%;
  left: 16%; right: 16%;
}
.zr-p, .zr-m {                         /* legends INSIDE the body */
  position: absolute;
  left: 0; right: 0;
  text-align: center;
  font: 400 0.78rem/1 var(--font-mono);
  color: var(--ink-2);
  pointer-events: none;
  z-index: 2;
}
.zr-p { top: 8px; }
.zr-m { bottom: 8px; }
#zoomthumb {
  position: absolute;
  left: 5px; right: 5px;
  height: 15px;
  border-radius: 8px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--acc) 30%, #fff) 0%,
    var(--acc) 55%,
    color-mix(in srgb, var(--acc) 80%, #4d4444) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.8),
    0 1px 3px rgba(77, 68, 68, 0.22);
  top: 50%;
  transform: translateY(-50%);
  transition: background 350ms ease;
  z-index: 3;                /* topmost — never clips under the gloss */
}
#rot {
  width: 54px; height: 54px;
  border-radius: 50%;
  /* protractor tick ring — cardinals heavier */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='54' height='54'%3E%3Cg stroke='%23a79d9d' stroke-width='1.4'%3E%3Cpath d='M27 3v6M51 27h-6M27 51v-6M3 27h6'/%3E%3C/g%3E%3Cg stroke='%23dad3d2' stroke-width='1'%3E%3Cpath d='M44 10l-3.5 3.5M44 44l-3.5-3.5M10 44l3.5-3.5M10 10l3.5 3.5'/%3E%3C/g%3E%3C/svg%3E") center / 54px 54px no-repeat,
    linear-gradient(180deg, #ffffff 0%, #f4f1f0 55%, #ece8e7 100%);
}
#rot::after {                          /* level reference at 12 o'clock */
  content: '';
  position: absolute;
  left: 50%; top: 2px;
  width: 1px; height: 6px;
  margin-left: -0.5px;
  background: var(--ink-3);
}
#rotmark {                             /* the needle — mirrors camera roll */
  position: absolute;
  left: 50%; top: calc(50% - 19px);
  width: 3px; height: 19px;
  margin-left: -1.5px;
  border-radius: 2px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--acc) 40%, #fff),
    var(--acc));
  box-shadow: 0 1px 2px rgba(77, 68, 68, 0.25);
  transform-origin: 50% 100%;
  rotate: 0deg;
  transition: background 350ms ease;
  z-index: 3;                /* topmost — never clips under the gloss */
}

/* ---------------- hint ---------------- */
#hint {
  position: fixed; left: 50%; bottom: 3.4rem;   /* clears the footer */
  transform: translateX(-50%);
  font: 400 0.72rem/1 var(--font-mono);
  letter-spacing: 0.22em;
  color: var(--ink);
  z-index: 10;
  animation: hint-pulse 2.4s ease-in-out infinite;
  transition: opacity 400ms;
}
#hint.gone { opacity: 0; pointer-events: none; }
@keyframes hint-pulse { 50% { opacity: 0.35; } }

/* the horizontal subway nav collapses into a vertical drop-down panel,
   opened by the hamburger; same train-line read, stacked as rows */
body.is-mobile header {
  height: calc(56px + env(safe-area-inset-top, 0px));
  top: 0;
  gap: 0.75rem;
  padding: env(safe-area-inset-top, 0px) 0.75rem 0;
}
body.is-mobile .wordmark-img {
  height: calc(100% - env(safe-area-inset-top, 0px));
  margin-left: -0.75rem;
  max-width: min(45vw, 180px);
  object-fit: contain;
}
body.is-mobile .nav-toggle {
  display: inline-flex;
  width: 48px;
  height: 48px;
  margin-left: auto;
  padding: 0;
  border: 1px solid var(--chrome-edge);
  border-radius: 8px;
  background: var(--paper);
  box-shadow: 0 8px 18px -16px rgba(95, 86, 86, 0.48);
}
body.is-mobile .nav-toggle-lbl {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

body.is-mobile nav#stops-nav {
  --rail-x: 23px;          /* x of the vertical rail = dot centre − 1px */
  position: absolute;
  top: 100%; left: 0; right: 0;
  flex-direction: column;
  align-items: stretch;
  align-self: auto;
  margin-left: 0;
  gap: 0;
  padding: 0;
  overflow-x: hidden;
  background: rgba(255, 255, 255, 0.97);   /* blur dropped (see header) */
  border-bottom: 1px solid var(--line);
  box-shadow: 0 14px 28px -18px rgba(0, 0, 0, 0.35);
  /* collapsed by default */
  max-height: 0;
  overflow-y: hidden;
  visibility: hidden;
  transition: max-height 260ms var(--ease-out), visibility 0s linear 260ms;
}
body.is-mobile header.nav-open nav#stops-nav {
  max-height: min(72vh, 560px);
  padding: 0.25rem 0 calc(0.7rem + env(safe-area-inset-bottom));
  overflow-y: auto;
  visibility: visible;
  transition: max-height 280ms var(--ease-out);
}

/* the horizontal journey line + riser don't apply in the vertical layout */
body.is-mobile .nav-line, body.is-mobile .nav-riser { display: none; }

/* stops stack as full-width rows: station dot on the rail, label right */
body.is-mobile .chip {
  width: 100%;
  justify-content: flex-start;
  min-height: 52px;
  padding: 16px 18px;
  gap: 16px;
  font-size: 0.86rem;
  letter-spacing: 0.14em;
  border-radius: 0;
  touch-action: manipulation;
}
body.is-mobile .chip .lbl { display: inline; }      /* labels return in the drop-down */

/* THE RAIL — drawn per row as a full-height 2px segment at a shared x,
   so abutting rows form one continuous line with no gaps or seams. Grey
   by default; takes the station's colour once the train has passed it.
   Ends trimmed to the first/last dot so nothing dangles past the end. */
body.is-mobile .chip::before {
  content: "";
  position: absolute;
  left: var(--rail-x);
  top: 0; bottom: 0;
  width: 2px;
  background: var(--line-2);
  transition: background 220ms var(--ease-out);
  pointer-events: none;
  z-index: 0;
}
body.is-mobile .chip.passed::before, body.is-mobile .chip.visited::before { background: var(--c); }
body.is-mobile .chip:first-child::before { top: 50%; }      /* begin at the first dot */
body.is-mobile .chip:last-child::before { bottom: 50%; }   /* end at the last dot */

/* STATIONS — sit on top of the rail (z 2). Hollow ring before the train
   reaches them, solid in colour once passed, a bold ring for the current
   stop. The dot covers the rail seam cleanly. */
body.is-mobile .chip .sq {
  position: relative;
  z-index: 2;
  flex: none;
  width: 13px; height: 13px;
  border: 2px solid var(--line-2);
  background: var(--paper);
}
body.is-mobile .chip.visited .sq, body.is-mobile .chip.passed .sq { border-color: var(--c); }
body.is-mobile .chip.passed .sq { background: var(--c); }
body.is-mobile .chip.active {
  background: color-mix(in srgb, var(--c) 8%, var(--paper));
  box-shadow: inset 3px 0 0 var(--c);
  border-radius: 0;
}
body.is-mobile .chip.active .sq { background: var(--paper); border-width: 3px; border-color: var(--c); }

/* Wafer content remains viewport-invariant; only chrome adapts here. */
/* the coordinate readout is desktop-only chrome */
body.is-mobile #foot { display: none; }
/* photos are content, not assets to drag off the page: kill the browser's
   default image drag-ghost, text selection, and the iOS long-press callout
   so clicking the globe/team photo never starts a drag or a save menu */
img {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
