/* bluehourlabs.co
   ---------------------------------------------------------------------------
   The brand is six colour roles and two type families over the scrollcraft
   floor. Everything else is composition.

   The palette is taken from the sky the page actually paints: --sc-canvas is
   the zenith at eighteen degrees of depression, and --sc-accent is the warm
   band on the horizon at sunrise. They are not chosen next to the render, they
   are read out of it, so the chrome can never drift away from the world. */

@font-face {
  font-family: "Archivo"; font-style: normal; font-weight: 300 700;
  font-display: swap; src: url("fonts/archivo.woff2") format("woff2");
}
@font-face {
  font-family: "Geist"; font-style: normal; font-weight: 200 700;
  font-display: swap; src: url("fonts/geist.woff2") format("woff2");
}
@font-face {
  font-family: "Geist Mono"; font-style: normal; font-weight: 300 600;
  font-display: swap; src: url("fonts/geist-mono.woff2") format("woff2");
}

:root {
  --sc-canvas:     #04060B;
  --sc-surface:    #0C1120;
  --sc-ink:        #EFE9DF;
  --sc-ink-soft:   #C5CEDD;   /* tinted to the night, never a flat grey */
  --sc-accent:     #F5A24A;   /* the warm band on the horizon at sunrise */
  --sc-accent-ink: #1A1206;

  --sc-font-display: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sc-font-text:    "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sc-font-mono:    "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Where the copy sits, and therefore where the scrim has to be. The horizon
     lives at 82% of the frame, so copy stops above the twilight arch instead of
     landing in the one part of the sky that gets bright. */
  --copy-bottom: clamp(7rem, 24vh, 15rem);
}

/* ------------------------------------------------------------ the world -- */

.world__sky {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  display: block;
}

/* Media-free waypoints. The engine writes opacity, visibility and z-index on
   these; there is nothing in them to see, and that is the point. */
.sc-world__seg { pointer-events: none; }

/* ------------------------------------------------------------- the copy -- */

.copy { max-width: min(46ch, 78vw); }
.copy--lead  { left: var(--sc-gutter); right: auto; bottom: var(--copy-bottom); }
.copy--trail { left: auto; right: var(--sc-gutter); bottom: var(--copy-bottom); text-align: right; }
.copy--table { left: var(--sc-gutter); right: auto; bottom: var(--copy-bottom); max-width: min(34ch, 80vw); }
/* The close is the one block that resolves onto the land rather than into the
   sky: the ground below the horizon is the only part of the frame that is dark
   at every hour, so the last thing on the page is also the most legible. */
/* The one block anchored to the top of the frame. See index.html. */
.copy--high  { left: var(--sc-gutter); right: auto; top: clamp(5rem, 16vh, 11rem); bottom: auto; }
.copy--close { left: var(--sc-gutter); right: auto; bottom: clamp(2rem, 5vh, 3.5rem); max-width: 40ch; }

.wordmark {
  font-family: var(--sc-font-display);
  font-weight: 600;
  font-size: clamp(3rem, 11vw, 9.5rem);
  line-height: 0.88;
  letter-spacing: -0.045em;
  margin: 0 0 var(--sc-5);
  text-wrap: balance;
}
.lede {
  font-size: var(--sc-t-lg);
  line-height: 1.42;
  letter-spacing: -0.012em;
  max-width: 30ch;
  margin: 0 0 var(--sc-5);
  color: var(--sc-ink);
  text-wrap: pretty;
}
.head {
  font-family: var(--sc-font-display);
  font-weight: 500;
  font-size: clamp(1.75rem, 4.2vw, 3.4rem);
  line-height: 1.04;
  letter-spacing: -0.032em;
  margin: 0 0 var(--sc-4);
  text-wrap: balance;
}
.head--big { font-size: clamp(2.4rem, 7vw, 5.6rem); letter-spacing: -0.042em; }

/* Light type on a dark ground needs compensating on three axes: a touch more
   line height, a touch more tracking, and one step more weight. Body copy set
   with light-mode metrics reads thin and slightly blurred here. */
.say {
  font-size: var(--sc-t-base);
  font-weight: 350;
  line-height: 1.68;
  letter-spacing: 0.004em;
  color: var(--sc-ink);
  margin: 0;
  text-wrap: pretty;
}
.quiet {
  font-size: var(--sc-t-lg);
  font-weight: 300;
  line-height: 1.44;
  letter-spacing: -0.006em;
  color: var(--sc-ink);
  margin: 0;
  max-width: 24ch;
  margin-left: auto;
}

.live {
  font-family: var(--sc-font-mono);
  font-size: var(--sc-t-xs);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sc-ink-soft);
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.live__sep { display: inline-block; width: 1.6em; }
.live__sep::before { content: "·"; }
[data-live-alt] { color: var(--sc-accent); }

/* The almanac. Monospace here is not a costume for "technical": these are
   tabulated figures that have to line up in a column, which is the one job the
   face is actually for. */
.almanac {
  margin: 0 0 var(--sc-4);
  font-family: var(--sc-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--sc-t-sm);
}
.almanac > div {
  display: flex; align-items: baseline; gap: var(--sc-4);
  padding: var(--sc-2) 0;
  border-top: 1px solid var(--sc-hairline);
}
.almanac > div:last-child { border-bottom: 1px solid var(--sc-hairline); }
.almanac dt {
  flex: 1 1 auto;
  color: var(--sc-ink-soft);
  letter-spacing: 0.08em; text-transform: uppercase;
  font-size: 0.82em;
}
.almanac dd {
  margin: 0; flex: 0 0 auto;
  color: var(--sc-ink); font-weight: 500;
  font-size: 1.15em; letter-spacing: 0.01em;
}
.almanac dd.is-absent {
  color: var(--sc-ink-soft); font-size: 0.9em;
  letter-spacing: 0.04em; font-weight: 400;
}
.foot-note {
  margin: 0; font-size: var(--sc-t-xs); color: var(--sc-ink-soft);
  letter-spacing: 0.02em;
}

.colophon__mark {
  font-family: var(--sc-font-display);
  font-weight: 500; font-size: var(--sc-t-xl);
  letter-spacing: -0.022em; margin: 0 0 var(--sc-2);
}
.colophon__line {
  margin: 0 0 var(--sc-2);
  font-size: var(--sc-t-sm); color: var(--sc-ink);
  letter-spacing: 0.01em;
}
.colophon__line--dim {
  color: var(--sc-ink-soft); font-size: var(--sc-t-xs);
  max-width: 42ch; line-height: 1.6; margin-top: var(--sc-4);
}

/* ------------------------------------------------------------- the scrim --
   Not a full-frame overlay. A band that is transparent above the type and
   carries density only where the type sits, plus a second, tighter pass on the
   land where the close resolves. Measured on the composited render by the
   verification harness, not tuned by eye. */
.plate {
  /* The frame-wide scrim is gone. It could not be right: the same page is
     astronomical night at one end and full daylight at the other, so any fixed
     shape is either a wash over the stars or nothing at all at midday. All it
     keeps is a whisper on the land, where the close resolves. */
  background: linear-gradient(to top,
    color-mix(in oklab, var(--sc-canvas) 55%, transparent) 0%,
    transparent 22%);
}

/* One per copy block, sized and positioned by page.js, carrying that block's
   own opacity. It is deliberately much larger than the type it protects: a
   plate whose edge you can find is a card, and this has to read as the sky
   being a little deeper where the words are.
   `--sky-lum` is the frame's own measured luminance, published by the renderer,
   so this does almost nothing over a night sky and most of its work over a
   bright one. A fixed strength has to be tuned for the brightest frame on the
   page and is then a grey wash over the stars. */
.plate-one {
  position: absolute;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  /* A feathered box, not an ellipse. An ellipse inscribed in a text block's
     bounding rectangle leaves the corners uncovered, which is exactly where the
     end of the longest line sits, so the one line that most needed the plate
     was the one line getting none of it. Two axis masks intersected give a
     rectangle with soft edges, which is the shape a paragraph actually is. */
  background: color-mix(in oklab, var(--sc-canvas) calc(64% * var(--sky-strength, 0.4)), transparent);
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0%, #000 30%, #000 70%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 30%, #000 70%, transparent 100%);
  mask-image:
    linear-gradient(to right,  transparent 0%, #000 30%, #000 70%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 30%, #000 70%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
:root { --sky-strength: clamp(0.30, calc(0.20 + 1.6 * var(--sky-lum, 0.2)), 1); }

/* -------------------------------------------------------------- the map --
   The grammar asks for a map rather than a nav, because a world you cannot
   skip around in is a video. Every waypoint is a real link and the whole thing
   is operable from the keyboard. */
/* The spine. Without it the ticks read as unrelated marks; with it the map
   reads as one route with stops on it, which is what it is. */
.route::before {
  content: ""; position: absolute;
  right: 0; top: var(--sc-2); bottom: var(--sc-2);
  width: 1px; background: var(--sc-ink-soft); opacity: 0.22;
}
.route {
  position: fixed;
  right: clamp(0.9rem, 2.4vw, 2.2rem);
  top: 50%; translate: 0 -50%;
  z-index: var(--sc-z-chrome);
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sc-2) 0;
}
.route__leg {
  display: flex; align-items: center; justify-content: flex-end;
  min-width: 22px;
  gap: var(--sc-3);
  padding: 5px 0;
  text-decoration: none; color: var(--sc-ink-soft);
  font-family: var(--sc-font-mono);
  font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
}
.route__name {
  order: -1;
  opacity: 0; translate: 4px 0;
  transition: opacity 180ms var(--sc-ease-out), translate 180ms var(--sc-ease-out), color 180ms var(--sc-ease-out);
  white-space: nowrap;
}
.route__tick {
  display: block; width: 22px; height: 1px; flex: 0 0 auto;
  background: currentColor; opacity: 0.55;
  transition: width 200ms var(--sc-ease-out), opacity 200ms var(--sc-ease-out);
}
.route__leg[aria-current="true"] { color: var(--sc-ink); }
.route__leg[aria-current="true"] .route__tick { width: 40px; opacity: 1; }
.route__leg[aria-current="true"] .route__name { opacity: 1; translate: 0 0; }
@media (hover: hover) and (pointer: fine) {
  .route:hover .route__name { opacity: 1; translate: 0 0; }
  .route__leg:hover { color: var(--sc-ink); }
  .route__leg:hover .route__tick { width: 40px; opacity: 1; }
}
.route__leg:focus-visible .route__name { opacity: 1; translate: 0 0; }


/* --------------------------------------------------------------- misc --- */

.skip {
  position: fixed; left: 50%; top: 0; translate: -50% -140%;
  z-index: 200; background: var(--sc-accent); color: var(--sc-accent-ink);
  padding: var(--sc-2) var(--sc-4); border-radius: 0 0 var(--sc-r-sm) var(--sc-r-sm);
  font-size: var(--sc-t-sm); text-decoration: none;
}
.skip:focus-visible { translate: -50% 0; }

/* The escape hatch, opened by the skip link. It is revealed with :target rather
   than the hidden attribute, because an element carrying `hidden` stays hidden
   when you jump to it and the skip link would silently do nothing.
   The copy in the sky is real text in the document either way, in reading
   order, and the engine only ever writes opacity on it, never visibility, so a
   screen reader gets the whole page without coming through here. */
.plain { display: none; }
.plain:target {
  display: block;
  position: fixed; inset: 0; overflow: auto; z-index: 300;
  background: var(--sc-canvas);
  padding: var(--sc-8) var(--sc-gutter);
}
.plain:target > * { max-width: var(--sc-measure); }
.plain h2 { font-family: var(--sc-font-display); font-size: var(--sc-t-2xl); margin: 0 0 var(--sc-5); letter-spacing: -0.03em; }
.plain p { margin: 0 0 var(--sc-4); }

:where(a, button):focus-visible {
  outline: 2px solid var(--sc-accent);
  outline-offset: 4px;
  border-radius: 2px;
}

/* --------------------------------------------------------------- phone --- */
@media (max-width: 700px) {
  :root { --copy-bottom: clamp(5.5rem, 20vh, 11rem); }
  /* --sc-t-4xl floors at a desktop 3.4rem, which wraps a two-word wordmark to
     four lines at 390px. Step the hero down a rung instead. */
  .wordmark { font-size: clamp(2.6rem, 15vw, 4.2rem); }
  .head--big { font-size: clamp(2rem, 11vw, 3rem); }
  .copy, .copy--table { max-width: none; right: var(--sc-gutter); }
  /* Both corner anchors become full-width blocks at this size, so the corner
     scrim has nothing left to hug and has to become a band. It still stops
     well short of the top, and it still leaves the horizon alone. */
  /* A phone puts every block full-bleed, so the side feather goes: there are no
     sides left to feather into. */
  .plate-one {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 13%, #000 87%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 13%, #000 87%, transparent 100%);
  }
  .copy--trail { text-align: left; left: var(--sc-gutter); right: var(--sc-gutter); }
  .copy--high  { top: clamp(4rem, 12vh, 8rem); }
  .quiet { margin-left: 0; }
  /* The map keeps its full label list on a phone rather than collapsing to
     dots: a map you cannot read is not navigation. It moves to the bottom edge
     and lies down, where a thumb can reach it. */
  .route {
    top: auto; bottom: 0; right: 0; left: 0; translate: none;
    flex-direction: row; gap: 0;
    justify-content: space-between;
    padding: var(--sc-3) var(--sc-gutter) calc(var(--sc-3) + env(safe-area-inset-bottom));
    background: linear-gradient(to top, color-mix(in oklab, var(--sc-canvas) 82%, transparent), transparent);
  }
  .route::before { right: var(--sc-gutter); left: var(--sc-gutter); top: auto; bottom: calc(var(--sc-3) + env(safe-area-inset-bottom) + 8px); width: auto; height: 1px; }
  .route__leg { flex: 1 1 0; justify-content: center; padding: 6px 0; }
  .route__name { display: none; }
  .route__tick { width: 100%; max-width: 26px; height: 2px; }
  .route__leg[aria-current="true"] .route__tick { width: 100%; }
}

/* ------------------------------------------------------ reduced motion --- */
@media (prefers-reduced-motion: reduce) {
  /* Fewer and gentler, not zero. The sky still tracks the scroll, because the
     sky IS the content and deleting it would delete the page; what goes is the
     smoothing, so nothing keeps moving after the hand stops. Handled in
     page.js, where the lerp is set to 1. */
  .route__name, .route__tick { transition: none; }
}

/* The route rail is the position indicator. A second one across the top, in the
   accent, is duplicate navigation and the loudest thing on the page. */
[data-sc-progress] { display: none; }
