/* =============================================================================
   reveal.css — the closing curtain

   This file is an addition of this project, not part of the reference page: it
   is the one piece the comparisons in tools/ do not cover, because they strip
   the band before they measure (see stripClosingBand() in tools/lib/sites.mjs).
   It is the "curtain reveal" footer that trae.cn and
   hermes-agent.nousresearch.com use:
   the last band of the page is a brand panel pinned to the bottom of the
   viewport, the page shell above it is opaque, and the final stretch of
   scrolling lifts that shell off the panel — the giant wordmark is clipped by
   the shell's trailing edge until the very end of the document.

   The three layers, front to back:

     #top              z-index 1   the opaque shell: everything that scrolls
     .reveal           z-index 0   the runway. Its padding-bottom is the scroll
                                   distance over which the panel is uncovered;
                                   it paints nothing, so the panel shows
                                   through it
     .reveal__panel    fixed       the band itself, at the bottom of the window
     .footer           the last child of #root, after the runway: the one-line
                                   footer, in the strip the band leaves free
                                   below itself

   Runway and panel are both --reveal-h tall, so the last pixel of the document
   is exactly progress 1. src/js/reveal.js writes that progress to --reveal-p
   (0 → 1); the fade, the blur and the lift below are functions of that single
   number, and the same script sizes the wordmark to the panel.

   The band is also the one thing on the page to play with: the same script
   splits the wordmark into glyphs and drives them from a pointer field, so the
   letters can be dragged, wound with a click and leaned by a fast scroll. The
   rules for that are at the bottom of this file; they stay inert unless the
   script marks the panel .is-live.

   <html> carries data-footer-reveal="on". Without it the panel is display:none
   and the runway collapses, which is the plain replica again — the state the
   comparisons against the offline mirror expect.

   The band is only half of the effect. The shell now ends on the closing
   section (.outro, further down this file), so the band is unveiled off the
   back of a screen that belongs there — without it the shell's trailing edge
   is a straight line drawn across whatever the story happened to leave on
   screen, which is what made the band read as a separate thing rather than as
   the page continuing.

   Breakpoints live in responsive.css, which loads last (see its footer block).
   Reduced motion is handled in reveal.js, which leaves --reveal-p at 1.
   ============================================================================= */

/* --- off unless the page asks for it ------------------------------------- */

.reveal {
  display: none;
}

html[data-footer-reveal="on"] #top {
  position: relative;
  z-index: var(--z-shell);
  /* the shell has to be opaque, or the band shows through it */
  background: var(--page);
}

html[data-footer-reveal="on"] .reveal {
  display: block;
  position: relative;
  z-index: var(--z-reveal);
  padding-bottom: var(--reveal-h);
}

/* --- the band ------------------------------------------------------------ */

.reveal__panel {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  display: grid;
  place-items: center;
  height: var(--reveal-h);
  overflow: hidden;
  background: var(--ink);
  color: var(--page);
  /* inert until reveal.js wires the pointer field to it (.is-live, below):
     the links that matter are in .footer, under the band */
  pointer-events: none;
  user-select: none;
  /* a little travel as it arrives, the same idea as the reference's lift */
  transform: translateY(calc((1 - var(--reveal-p, 1)) * 2.4%));
}

.reveal__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  /* no padding: reveal.js measures this box and fills it with the wordmark */
  width: min(92%, 1600px);
  margin-inline: auto;
  text-align: center;
  opacity: calc(.25 + .75 * var(--reveal-p, 1));
  filter: blur(calc(8px * (1 - var(--reveal-p, 1))));
  /* the blur is re-rasterised while the band is being uncovered */
  will-change: opacity, filter;
}

.reveal__row {
  display: flex;
  /* N.B. the row has to keep shrinking to the wordmark: reveal.js measures it
     against the panel and writes --reveal-word-size from that ratio, so a
     width here would make the measurement meaningless. Centring is the inner
     box's job, below. */
  /* reveal.js measures the row and writes the exact size here. The fallback is
     the same fit expressed in CSS, for the first paint and for a page without
     scripts: 16.7vw fills a panel that is still growing with the viewport,
     291px is that fit once the panel stops at 1600px, and neither may exceed
     62% of the band's height. */
  font-size: var(--reveal-word-size, min(16.7vw, 291px, calc(var(--reveal-h) * .62)));
  line-height: .9;
  white-space: nowrap;
  /* --reveal-lean is written per frame while a scroll is fast */
  transform: translateY(calc((1 - var(--reveal-p, 1)) * .05em)) skewX(var(--reveal-lean, 0deg));
}

.reveal__word {
  font-weight: 790;
  letter-spacing: -.055em;
}

/* --- the footer, as the band's bottom row -------------------------------- */

/*
   The footer markup sits inside the panel, as the band's second grid row: the
   wordmark takes the free row above it and centres there, so the two are
   uncovered together — the closing line no longer slides in on its own at the
   end of the scroll.

   Scoped to the band being on. With data-footer-reveal="off" — which is also
   the state the comparisons in tools/ measure in — the footer goes back to the
   plain replica styling, light strip and hairline included.
*/

html[data-footer-reveal="on"] .reveal__panel {
  /* wordmark above, closing line below; the row above takes what is left */
  grid-template-rows: 1fr auto;
}

html[data-footer-reveal="on"] .footer {
  /* the panel is inert while it is only decorative; the closing line is not */
  pointer-events: auto;
  justify-self: stretch;
  align-self: end;
  width: 100%;
  border-top: 0;
  background: var(--ink);
  color: var(--page);
}

html[data-footer-reveal="on"] .footer__inner {
  padding: 22px max(24px, 3vw) 26px;
}

html[data-footer-reveal="on"] .footer__brand,
html[data-footer-reveal="on"] .footer__links a {
  /* the band's light, dialled back: secondary text on a dark surface */
  color: var(--page);
  opacity: .62;
}

html[data-footer-reveal="on"] .footer__links a {
  transition: opacity .18s, color .18s;
}

html[data-footer-reveal="on"] .footer__links a:hover {
  opacity: 1;
}

/* --- the wordmark as something to play with ------------------------------ */

.reveal__panel.is-live {
  pointer-events: auto;
  cursor: pointer;
}

/* One span per letter, created by reveal.js, so each can be moved on its own.
   The brand is a clockwork doll: the letters stand on the baseline and are
   dragged, skewed and squashed from there. */
.reveal__glyph {
  display: inline-block;
  transform-origin: 50% 100%;
  /* transformed on most frames while the pointer field settles */
  will-change: transform;
}

/* =============================================================================
   The bridge — .outro, and the story's trailing edge

   The story ends on an empty stage and the band below is opaque with a
   straight edge, so on their own the two cut against each other the moment
   the band starts to rise: the helix's last card is still half on screen when
   the stage's trailing edge arrives, and a light stage meets a black band. Two
   things fix that, and neither of them is the band itself.

     1. the story's own bottom edge. The sticky stage clips at its trailing
        edge, and the helix's last card is still half on screen when the stage
        starts to slide away, so the card is cut off in a straight line. The
        fade at the bottom of the stage below dissolves whatever is left into
        the wash the stage ends on. story.js writes how much of it is on to
        --stage-fade, so it is invisible for the whole story and only arrives
        with the last chapter's exit.

     2. what the band is uncovered against. That is this section: it carries
        the closing screen, and its own background is the colour the story
        ends on, so the story's trailing edge moves up through the section
        without drawing a line. The curtain is then unveiled straight off the
        back of the section — the straight edge below is the band arriving, not
        a seam between two unrelated blocks.

   The section is deliberately short: the closing screen, then one breath of
   runway. Its bottom padding is what decides how much of that screen is left
   on view while the band is already coming up (at 1440×900 the download button
   stays 216px clear of the band's edge at the end of the scroll), so that is
   the number to move if the arrival feels early or late.

   Both parts go with the curtain: without data-footer-reveal="on" the section
   is not rendered and the stage keeps its hard edge, which is the plain
   replica. The comparisons in tools/ strip the section before they measure
   (stripClosingBand() in tools/lib/sites.mjs).
   ============================================================================= */

html:not([data-footer-reveal="on"]) .outro {
  display: none;
}

.outro {
  position: relative;
  /* top gutter, side gutters, then the short runway the band is uncovered
     across — everything the visitor sees between the buttons and the wordmark */
  padding: 20svh max(24px, 6vw) 24svh;
  /* the colour the story ends on, so the stage's trailing edge has nothing to
     draw a line against. The story is the sticky stage when it is animated, and
     the page itself when it is the plain one, so main.js says which is on. */
  --outro-base: var(--surface-lo);
  background: var(--outro-base);
}

/* the plain story (reduced motion, or a short landscape window) ends on the
   page's own colour rather than on the stage's wash */
html[data-footer-reveal="on"][data-story="static"] .outro {
  --outro-base: var(--page);
}

/* --- the closing screen -------------------------------------------------
   Laid out like the reference page's own ending (.ending in the reference
   stylesheet): one centred column, icon → title → version → download, the
   type scale and the accent pill taken from there. The one thing that does
   not come from it is the background: the reference's ending sits on
   --scene, which is a step away from the wash the story ends on, and this
   section is the surface the band is unveiled across, so it stays on the
   story's own colour (--outro-base) instead.
   ------------------------------------------------------------------------ */

.outro__inner {
  max-width: 900px;
  margin-inline: auto;
  text-align: center;
}

/* the app icon, the same raster the bar wears, at its own pixel size */
.outro__mark {
  display: block;
  width: clamp(88px, 12vh, 128px);
  height: auto;
  margin: 0 auto 3vh;
  user-select: none;
}

.outro__title {
  font-size: clamp(34px, 6.8vw, 96px);
  font-weight: 790;
  line-height: 1;
  letter-spacing: -.06em;
  /* the wordmark stays on one line; responsive.css lets it wrap on phones */
  white-space: nowrap;
}

.outro__version {
  margin: 2.8vh 0 3.4vh;
  color: var(--ink-2);
  font-size: 14px;
}

.outro__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}

/* the reference's ending is the one place on the page that wears the accent
   fill; --on-accent is the colour that sits on it in every colourway */
.outro__cta {
  background: var(--accent);
  color: var(--on-accent);
  padding: 17px 36px;
  font-size: 16.5px;
}

.outro__cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 30px var(--accent-shadow-strong);
}

/* a disabled CTA ("下载暂不可用") must not lift either */
.outro__cta:disabled:hover {
  transform: none;
  box-shadow: none;
}

/* --- the story's trailing edge ------------------------------------------ */

html[data-footer-reveal="on"] .stage-sticky::before {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  /* over the cards (1) and the copy (2) */
  z-index: 3;
  height: 14svh;
  background: linear-gradient(180deg, transparent, var(--surface-lo));
  /* story.js raises this as the last chapter leaves; without scripts, or
     before that point, nothing is painted here at all */
  opacity: var(--stage-fade, 0);
  pointer-events: none;
}
