/* Home: one screen, no scroll. The stage (3D or its still) fills the screen
   behind the UI. The 3D module reads .stage__frame for where the mark sits. */

:root {
  /* The mark's width / height. main.js updates it from the loaded mark file. */
  --mark-aspect: 1.0024;
  /* One rule for every screen: the mark fills the free space between the
     header and the top of the Home line, with the same clear space above and
     below it. main.js measures the line, buttons and corner (--home-ui-h);
     --home-ui-guess stands in until it has, or for good without scripts. */
  --home-ui-guess: 28dvh;
  --mark-clear: clamp(24px, 4dvh, 64px);
  --mark-top: calc(var(--header-h) + env(safe-area-inset-top, 0px));
  --mark-free: calc(100dvh - var(--mark-top) - var(--home-ui-h, var(--home-ui-guess)));
  --mark-max-w: 46vw;
  --mark-w: clamp(180px, min(var(--mark-max-w), (var(--mark-free) - 2 * var(--mark-clear)) * var(--mark-aspect), 62dvh), 1200px);
  --mark-cx: 50%;
  --mark-cy: calc(var(--mark-top) + var(--mark-free) / 2);
}
/* Portrait: up to the full width between the gutters. */
@media (max-aspect-ratio: 3/4) {
  :root { --mark-max-w: calc(100vw - var(--gutter-l) - var(--gutter-r)); }
}
/* On phones the line and buttons take more of the height (35 to 42%, and
   about half on a 320 x 568 screen, where the buttons stack), so the stand-in
   is larger there. That keeps the mark clear of the line without scripts. */
@media (max-aspect-ratio: 3/4) and (max-width: 759px) {
  :root { --home-ui-guess: 40dvh; }
}
@media (max-aspect-ratio: 3/4) and (max-width: 344px) and (max-height: 700px) {
  :root { --home-ui-guess: 50dvh; }
}
@media (max-height: 520px) and (orientation: landscape) {
  :root {
    --mark-w: min(39vw, calc(56dvh * var(--mark-aspect)));
    --mark-cx: 26%;
    --mark-cy: 55%;
  }
}

/* Home locks to the screen. Short screens (200% zoom, landscape phones) scroll. */
html.is-home body { overflow: hidden; }
@media (max-height: 479px) {
  html.is-home body { overflow: auto; }
}

/* ---------- Stage ---------- */
/* Cream from the first paint (r6, Bryce 2026-11-04: no dark backgrounds, the
   cream is the site's ground). The stage is the page's own cream, the 3D
   canvas is transparent over it, and inside the badge's ring the cream enamel
   lands on this same cream at rest, so the logo sits on the page. */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--color-bg);
  view-transition-name: stage;
  touch-action: pan-y;
}
/* The stage shows while it holds the mark. Parked, the header logo takes over. */
html.js:not(.stage-unparked) .stage { visibility: hidden; }
.stage.is-parking { z-index: 40; background: transparent; pointer-events: none; }
.stage.is-parked { background: transparent; pointer-events: none; }

.stage__studio {
  position: absolute;
  inset: 0;
  transition: opacity var(--vt-dur) var(--vt-ease);
}
.stage.is-parking .stage__studio,
.stage.is-parked .stage__studio,
.stage.is-landing .stage__studio { opacity: 0; }
/* The shadow belongs to the badge at rest on Home. When the badge leaves for
   the header it goes at once (the still), or travels with the 3D badge while
   it fades (stage.js sets --shadow-fx, --shadow-fy and --shadow-fk from the
   flight); left behind it would sit there as a grey disc with no badge on it.
   It comes back once the badge has landed again (.is-landing is on for the
   flight Home, still.js and stage.js). */
.stage.is-parking .stage__studio,
.stage.is-landing .stage__studio { transition-duration: 0s; }
.stage.has-3d.is-parking .stage__studio { transition-duration: 140ms; }

/* The studio is the page itself: no pool, no vignette, no softbox strips (they
   only worked on the old dark stage). What is left is the badge's soft, warm
   contact shadow on the page, as if it stood a little off the paper with the
   light above and in front. It sits under the frame (same box as
   .stage__frame) and fades out as the badge flies to the header, which has
   none. The 3D sets --shadow-sx, --shadow-sy and --shadow-lx, so it narrows
   as the badge turns and grows with it as it arrives; the still leaves it at
   rest. Two soft layers: a tight one under the lower edge, a wide faint one. */
.stage__shadow {
  --shadow-rgb: 92 70 36;
  position: absolute;
  left: var(--mark-cx);
  top: var(--mark-cy);
  width: calc(var(--mark-w) * 1.16);
  aspect-ratio: var(--mark-aspect);
  pointer-events: none;
  transform:
    translate(-50%, -50%)
    translate(var(--shadow-fx, 0px), var(--shadow-fy, 0px))
    scale(var(--shadow-fk, 1))
    translate(calc(var(--shadow-lx, 0) * var(--mark-w) * 0.014), calc(var(--mark-w) * 0.04 * var(--shadow-sy, 1)))
    scale(var(--shadow-sx, 1), var(--shadow-sy, 1));
  background:
    radial-gradient(closest-side, rgb(var(--shadow-rgb) / 0.17) 0%, rgb(var(--shadow-rgb) / 0.17) 79%, rgb(var(--shadow-rgb) / 0.06) 86%, rgb(var(--shadow-rgb) / 0) 95%),
    radial-gradient(closest-side at 50% 58%, rgb(var(--shadow-rgb) / 0.05) 66%, rgb(var(--shadow-rgb) / 0) 100%);
  will-change: transform;
}

/* The still's first-load arrival (no 3D: no WebGL2, a software GPU, Save-Data,
   or the 3D later than 4 s), the same as the 3D's: the badge comes forward
   from a speck to full size, opaque in every frame, while its shadow comes up.
   It replaced motion.css's older still intro (an opacity fade, which would show
   a see-through navy badge on the cream; r7 took that rule out of motion.css).
   The selectors still carry .stage. Reduced motion never gets .stage-intro. */
@keyframes stage-arrive { from { transform: scale(0.04); } }
@keyframes stage-shadow-in { from { opacity: 0; } }
html.stage-intro .stage .stage__tilt { animation: stage-arrive 1150ms cubic-bezier(0.25, 1, 0.5, 1) both; }
/* The shadow grows with it (the whole studio, scaled about the badge's center). */
html.stage-intro .stage .stage__studio { transform-origin: var(--mark-cx) var(--mark-cy); animation: stage-arrive 1150ms cubic-bezier(0.25, 1, 0.5, 1) both; }
html.stage-intro .stage .stage__shadow { animation: stage-shadow-in 500ms var(--ease-out) both; }

/* The 3D stage's phone hint ("Drag to turn it.") is placed by placeHint() in
   js/stage/stage.js and styled in js/stage/stage.css. Nothing here. */

/* Where the mark sits. The 3D module fits its badge to this box. */
.stage__frame {
  position: absolute;
  left: var(--mark-cx);
  top: var(--mark-cy);
  width: var(--mark-w);
  aspect-ratio: var(--mark-aspect);
  transform: translate(-50%, -50%);
}
.stage__still {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  will-change: transform;
}
/* The flight into the header slot runs on the same clock and curve as the wipe. */
.stage__still.is-moving { transition: transform var(--vt-dur) var(--vt-ease); }
.stage__tilt {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}
/* Tilted only while the cursor moves it (still.js sets .is-tilting). At rest
   it has no transform at all: a perspective() layer, even at 0 degrees, is
   resampled on the GPU and drew the still visibly softer than the 3D, so the
   hand-over to the 3D read as a focus pull. */
.stage__tilt.is-tilting { transform: perspective(1600px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
.stage__mark {
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
}
.stage__sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  /* Warm ivory, like the 3D's light sweep since r7 (a white bar read cool
     lavender silver on the navy: 200, 203, 212; this lands near 207, 196, 189).
     A narrow bar like the 3D's (r7 fix round 2): the band was 42 to 58% of
     the 260% wide gradient, a milky wash over about 40% of the badge; it is
     46.5 to 53.5% now, about 18% of the badge, a little brighter at its core. */
  background: linear-gradient(104deg, rgb(255 236 214 / 0) 0%, rgb(255 236 214 / 0) 46.5%, rgb(255 236 214 / 0.8) 50%, rgb(255 236 214 / 0) 53.5%, rgb(255 236 214 / 0) 100%);
  background-size: 260% 100%;
  background-repeat: no-repeat;
  background-position: var(--sheen-x, 130%) 0;
  opacity: var(--sheen-o, 0);
}
.stage__sheen.is-sweeping { animation: sheen-sweep 1300ms var(--motion-ease-in-out) both; }
@keyframes sheen-sweep {
  0% { background-position: 130% 0; opacity: 1; }
  100% { background-position: -30% 0; opacity: 1; }
}
/* When the 3D canvas is live, the still hides under it. */
.stage.has-3d .stage__still { opacity: 0; transition: opacity 600ms var(--ease-out); }

/* The hand-off with the header logo during a view change (r7). The badge
   (the 3D canvas, or the still when there is no 3D) is its own layer in the
   view transition, drawn live with no animation of its own, so it flies
   between the header slot and Home whole, over the wipe: coming Home it leaves
   the slot from the first frame instead of waiting behind the stage's wipe.
   The header logo it replaces goes in that same frame: it gets its own layer
   too, with no snapshot of the old page's logo (that snapshot faded out over
   420 ms as a ghost in the slot while the badge flew away), and the live logo
   drops to 0 at once instead of fading. Leaving Home nothing changes: the
   header logo still fades in over the landed badge (.logo-handoff). Only one
   element carries the name at a time (has-3d), or the browser would skip the
   transition. The canvas's name is in js/stage/stage.css. */
.stage:not(.has-3d) .stage__still { view-transition-name: stage-badge; }
html.vt-to-home .site-header__logo { view-transition-name: header-logo; }
/* A tap on the header logo (the way Home on a phone) drew the browser's tap
   highlight, a tinted box, in the slot; it stayed there as the badge flew
   out of it. The focus ring is untouched. */
.site-header__logo { -webkit-tap-highlight-color: transparent; }
html.stage-unparked:not(.logo-handoff) .site-header__logo img { transition: none; }
::view-transition-group(stage-badge) { animation: none; }
::view-transition-old(stage-badge) { display: none; }
::view-transition-new(stage-badge) { animation: none; mix-blend-mode: normal; }
::view-transition-group(header-logo) { animation: none; }
::view-transition-old(header-logo) { display: none; }
::view-transition-new(header-logo) { animation: none; mix-blend-mode: normal; }
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ---------- Home UI ---------- */
.view--home {
  position: relative;
  height: 100vh;
  height: 100dvh;
  min-height: 480px;
  display: grid;
  grid-template-rows: 1fr auto;
  /* Side edges line up with the header and the .wrap column on wide screens. */
  padding: calc(var(--header-h) + env(safe-area-inset-top, 0px)) var(--edge-r) calc(clamp(var(--space-5), 1rem + 2vw, var(--space-7)) + env(safe-area-inset-bottom, 0px)) var(--edge-l);
  pointer-events: none;
  view-transition-name: home-ui;
}
.view--home a,
.view--home button { pointer-events: auto; }

.home__bottom {
  grid-row: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6) var(--space-8);
}
/* The Home line (r8): Barlow Semi Condensed 800 in capitals, the line the
   Barlow card on Bryce's options page showed, at its own sizes (56 px at
   1440, 88 at 2560, 34 on a 390 phone). The capitals come from
   text-transform; the words stay as written. Its box was 25ch (16ch on
   phones) in the font before r8; a ch of Barlow Semi Condensed is narrower,
   so 34ch and 22ch keep the box the same width in px (SYSTEM.md's 1.36),
   written in the web font's ch (--ch-display, base.css). */
.home__line {
  max-width: calc(34 * var(--ch-display));
  font-family: var(--font-display);
  font-size: clamp(2rem, 1.15rem + 2.9vw, 3.5rem); /* 56 at 1440, then 88 at 2560 (below) */
  font-weight: var(--font-weight-extrabold);
  text-transform: uppercase;
  letter-spacing: 0;
  line-height: 0.95;
  text-wrap: balance;
}
.home__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: clamp(var(--space-5), 1rem + 1.5vw, var(--space-6));
}
.home__corner {
  position: relative;
  flex: none;
  max-width: calc(24 * var(--ch-text)); /* 24ch of Barlow 400: the box sets where the line sits, so it holds in the fallback too (base.css) */
  padding-top: var(--space-4);
  color: var(--color-text-muted);
  text-align: left;
}
/* The hairline over the corner line draws in during the intro (motion.css). */
.home__corner::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--border-hairline);
  background: var(--color-line);
  transform-origin: left;
}

@media (min-width: 1440px) {
  .home__line { font-size: clamp(3.5rem, 0.929rem + 2.857vw, 5.5rem); }
  .home__corner { max-width: calc(26 * var(--ch-text)); font-size: var(--type-body-size); line-height: var(--type-body-leading); }
}

@media (max-width: 759px) {
  .home__bottom { flex-direction: column; align-items: stretch; gap: var(--space-5); }
  .home__line { font-size: clamp(1.75rem, 1rem + 4.6vw, 2.5rem); max-width: calc(22 * var(--ch-display)); }
  .home__ctas { display: grid; grid-template-columns: 1fr; }
  .home__ctas .btn { width: 100%; }
  .home__corner { max-width: none; padding-top: var(--space-3); }
}
/* Short phones (an iPhone SE with Safari's bars): buttons side by side and a
   smaller line, so the mark keeps a useful size above them. Each button is at
   least as wide as its one-line label, so on the narrowest phones (320 px, or
   Display Zoom) the second one drops under the first instead of running off
   the screen. Side by side they share the row evenly. */
@media (max-width: 759px) and (max-height: 700px) {
  .home__bottom { gap: var(--space-4); }
  .home__line { font-size: clamp(1.5rem, 0.9rem + 4vw, 2rem); }
  .home__ctas { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
  .home__ctas .btn { flex: 1 1 0; min-width: max-content; white-space: nowrap; padding-inline: var(--space-3); }
}
@media (max-width: 759px) and (min-height: 760px) {
  .home__ctas { margin-top: var(--space-6); }
}
@media (max-height: 520px) and (orientation: landscape) {
  /* Landscape phones: two columns that fit the screen. It only scrolls if the
     words truly don't fit (200% zoom). */
  .view--home { min-height: 0; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; align-items: center; }
  .home__bottom { grid-column: 2; grid-row: 1; flex-direction: column; align-items: flex-start; gap: var(--space-4); }
  .home__line { font-size: clamp(1.5rem, 1rem + 2vw, 2.25rem); }
  /* The buttons stay on one row, so the words' column is as wide as that row
     and the page stays one screen tall. main.js fits the mark to what's left. */
  .home__ctas { flex-wrap: nowrap; margin-top: var(--space-4); }
  .home__ctas .btn { flex: none; width: auto; min-width: 0; }
}

/* Line reveal: each word rises behind a mask, one line at a time. The .w and
   .w__i masks are shared with the view headlines (motion.css). */
