/* Landing page (spec §4), disclosure dialog (spec §4), greeting sequence (spec §5.3).
   Loaded on the logged-out landing page and /disclosure, and on the
   logged-in /?arrive=1 render (the no-JS greeting overlay). Tokens only: no hex. */
body.landing{min-height:100vh;}
.sitehead-landing{justify-content:space-between;position:relative;z-index:2;}

/* Rosette (spec §3.4, overridden 2026-09-21 "Task 11" — see spec §15): the decorative guilloche sits INSIDE the content
   column, right-aligned to the container's right edge under the "API DISCLOSURE" link, its top just below the header
   rule (so it is centred on the headline / tagline / key-plate block), instead of hugging the viewport edge.
   `.landing .wrap` is its containing block; overflow-x:clip stops it ever causing horizontal scroll. --ros is the box
   size (350px on desktop = the hero block height; the drawn rings sit 10/400 inside it). arrive.js measures it with getBoundingClientRect, so the login glide
   and shrink work from wherever it rests. */
body.landing .wrap{position:relative;overflow-x:clip;}
.rosette{--ros:350px;position:absolute;width:var(--ros);height:var(--ros);z-index:0;pointer-events:none;opacity:.55;}
.rosette[hidden]{display:none;}
body.landing .rosette{top:88px;right:calc(24px - var(--ros) * .025);}
.rosette svg{display:block;width:100%;height:100%;overflow:visible;}
.rosette .ring{fill:none;transform-origin:200px 200px;will-change:transform;}
.rosette .ring-a{stroke:var(--accent);stroke-width:.7;opacity:.6;animation:ros-spin 90s linear infinite,ring-in 1.4s ease-out backwards;}
.rosette .ring-b{stroke:var(--ink);stroke-width:.5;opacity:.3;animation:ros-spin 70s linear infinite reverse,ring-in 1.4s .2s ease-out backwards;}
.rosette .crest{fill:none;stroke:var(--ink);stroke-width:1.1;stroke-linecap:round;stroke-linejoin:round;}
.rosette .crest path,.rosette .crest line{stroke-dasharray:140;animation:crest-draw 2.4s ease-out both;}
@keyframes ros-spin{to{transform:rotate(360deg);}}
/* backwards fill only: it must not hold opacity after the load fade (the login glide owns it). */
@keyframes ring-in{from{opacity:0;}}
@keyframes crest-draw{from{stroke-dashoffset:140;}to{stroke-dashoffset:0;}}

/* Composition fix (2026-09-24): at >=1000px the card and the rosette used to read as two disconnected
   blocks — the rosette pinned to .wrap's far right edge left a wide dead gap past the (much narrower)
   key-plate, and its fixed top offset sat higher than the card so the pair never looked aligned. Above
   1000px the rosette drops out of absolute positioning and joins the card as a normal-flow flex pair,
   centred as one group with a fixed gap and vertically centred against each other — the .rosette top/right
   offsets below (tablet/phone) are untouched since position:static makes them inert here. */
@media (min-width:1000px){
  .hero-row{display:flex;align-items:center;justify-content:center;gap:56px;}
  .hero-row .rosette{position:static;flex:0 0 auto;}
}

/* Hero */
.hero{position:relative;z-index:1;padding:56px 0 24px;max-width:560px;display:flex;flex-direction:column;gap:14px;}
.hero>*{animation:hero-rise .7s ease-out both;}
.hero>*:nth-child(2){animation-delay:.08s;}
.hero>*:nth-child(3){animation-delay:.16s;}
.hero>*:nth-child(4){animation-delay:.24s;}
@keyframes hero-rise{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}
.hero-title{font:500 clamp(44px,8vw,64px)/1 var(--disp);letter-spacing:-.01em;margin:0;}
.hero-title .amp{color:var(--accent-ink);}
.hero-tag{font:italic 500 clamp(15px,2.2vw,18px)/1.35 var(--disp);color:var(--text2);margin:0 0 8px;max-width:26ch;}
.key-plate{border-color:var(--accent);padding:14px 16px;display:flex;flex-direction:column;gap:8px;max-width:380px;}
.key-plate .eb{font-family:var(--mono);font-size:9px;letter-spacing:.2em;color:var(--text2);text-transform:uppercase;}
.key-row{display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--ink);padding-bottom:6px;}
.key-input{flex:1;min-width:0;background:transparent;border:0;color:var(--text);font:italic 500 15px var(--disp);padding:6px 0;}
.key-input::placeholder{color:var(--text2);}
/* Edge's native "reveal password" eye would sit inside the ENTER row; hide it (and the clear cross). */
.key-input::-ms-reveal,.key-input::-ms-clear{display:none;}
/* Ink (not the orange-ink page default) ring: the plate border and ENTER button already carry the accent. */
.key-input:focus,.key-input:focus-visible{outline:2px solid var(--ink);outline-offset:3px;}
.key-go{padding:8px 14px;font-size:11px;}
.key-plate .error{padding:8px 10px;font-size:12px;line-height:1.4;}
body.is-leaving{overflow:hidden;}
body.is-leaving .hero{opacity:0;transform:translateY(-14px);transition:opacity .6s ease,transform .6s ease;pointer-events:none;}
/* Waiting on a slow /identify: the acorn breathes while the rosette idles at centre. */
.rosette.is-waiting .crest{animation:crest-pulse 1.1s ease-in-out infinite alternate;}
@keyframes crest-pulse{to{opacity:.35;}}
/* Header mark drops into its slot at the end of the login sequence (mark opacity is 1 at rest, so fade FROM 0). */
.mk.mk-in .mark{animation:mark-in .35s ease both;}
@keyframes mark-in{from{opacity:0;}}

/* Disclosure dialog (native <dialog>: focus trap, Esc and backdrop are free) */
/* `.plate{position:relative}` (app.css) outranks the UA dialog:modal{position:fixed}, so restore it.
   overflow stays visible so the .plate corner crosses (-6px) are not clipped; long content scrolls
   in the inner .disc-scroll wrapper instead. */
dialog.disclosure{position:fixed;inset:0;margin:auto;height:fit-content;max-height:none;overflow:visible;
  color:var(--text);padding:18px 22px;max-width:min(720px,calc(100vw - 32px));width:100%;}
/* margin-top clears the absolutely-positioned close button so it never overlaps the scrollbar. */
.disc-scroll{margin-top:18px;max-height:calc(100vh - 120px);max-height:calc(100dvh - 120px);overflow:auto;}
.disclosure::backdrop{background:color-mix(in srgb,var(--ink) 45%,transparent);}
.disclosure .eb,.disclosure-page .eb{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:var(--text2);text-transform:uppercase;}
.disclosure .tos-note,.disclosure-page .tos-note{display:block;margin-top:4px;letter-spacing:.06em;text-transform:none;}
.disclosure-page{padding:22px;max-width:720px;}
.disc-close{margin:0;position:absolute;right:10px;top:8px;}
.disc-x{background:none;border:0;font-size:14px;cursor:pointer;color:var(--text);padding:6px;}
.disc-list{margin:12px 0 0;}
.disc-row{display:grid;grid-template-columns:170px 1fr;gap:12px;padding:8px 0;border-bottom:1px dotted var(--border);}
.disc-row:last-child{border-bottom:0;}
.disc-row dt{font:500 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--text2);padding-top:3px;}
.disc-row dd{margin:0;font-size:13.5px;line-height:1.5;color:var(--text);}

/* Greeting (spec §5.3). Times are seconds after the greeting starts (= the glide finishing). */
.greeting{position:fixed;inset:0;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;
  text-align:center;pointer-events:none;padding:0 16px;}
.greeting[hidden]{display:none;}
/* Login only (arrive.js sets both the class and the inline `top`, in px, from where the rosette already
   sits): the box shrinks to its own content height and is vertically centred on that point instead of the
   full-viewport centre above — otherwise the whole sequence visibly slides down a tall viewport (2026-09-24).
   The welcome-back replay on home keeps the plain full-viewport centring; its own rosette already starts at
   the viewport centre by design (see .rosette-replay below), so nothing here applies to it. */
.greeting.anchor-top{bottom:auto;transform:translateY(-50%);}
.greeting.overlay{background:var(--bg);}
.greeting-welcome{margin:0;opacity:0;font:400 10px var(--mono);letter-spacing:.34em;color:var(--text2);}
.greeting-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.greeting-name{width:min(560px,92vw);height:auto;overflow:visible;clip-path:inset(0 100% 0 0);}
.greeting-name .hatch-line{stroke:var(--accent);stroke-width:1;fill:none;}
.greeting-name text{font-family:var(--disp);font-weight:700;fill:url(#hatch);stroke:var(--ink);stroke-width:1.4;}
.greeting-rule{width:min(360px,60vw);height:1px;border:0;background:var(--ink);margin:0;opacity:0;transform:scaleX(0);}
.greeting-serial{margin:0;opacity:0;font:400 11px/1.7 var(--mono);letter-spacing:.14em;color:var(--text);text-transform:uppercase;}
.greeting-serial .rank{color:var(--accent-ink);font-weight:500;}
/* Each segment keeps its own leading "· " glued to it (nbsp), so a wrap can only happen BEFORE a
   separator: a line may start with "· FALLOUT" but never ends with a dangling "·". */
.greeting-serial .seg{white-space:nowrap;}
.greeting.play .greeting-welcome{animation:g-fade .6s 0s forwards;}
.greeting.play .greeting-name{animation:g-sweep 1.9s .3s cubic-bezier(.45,0,.2,1) forwards;}
.greeting.play .greeting-rule{animation:g-rule .8s 2.1s forwards;}
.greeting.play .greeting-serial{animation:g-fade .8s 2.3s forwards;}
.greeting.overlay.play{animation:g-out .4s 2.05s forwards;}
/* Server-rendered (no-JS /?arrive=1) greeting: a SHORT version (spec §5.2), ~2.45s in total. The whole
   sequence is compressed so nothing is cut off; the JS path (no .overlay class) keeps the timings above,
   which arrive.js's 5.5s/6.0s handoff depends on. */
.greeting.overlay.play .greeting-welcome{animation:g-fade .4s 0s forwards;}
.greeting.overlay.play .greeting-name{animation:g-sweep 1.2s .2s cubic-bezier(.45,0,.2,1) forwards;}
.greeting.overlay.play .greeting-rule{animation:g-rule .4s 1.2s forwards;}
.greeting.overlay.play .greeting-serial{animation:g-fade .4s 1.4s forwards;}
@keyframes g-fade{to{opacity:1;}}
@keyframes g-sweep{to{clip-path:inset(0 0 0 0);}}
@keyframes g-rule{to{opacity:.6;transform:scaleX(1);}}
@keyframes g-out{to{opacity:0;visibility:hidden;}}

/* The acorn inside the big rosette fades out before the name is engraved over it and
   returns as the rosette shrinks away (0.3s out, back at 3.3s to 3.9s of 4.6s). */
.rosette.is-greeting .crest{animation:crest-cycle 4.6s linear forwards;}
@keyframes crest-cycle{0%{opacity:1;}6.5%,71.7%{opacity:0;}84.8%,100%{opacity:1;}}

/* Welcome-back replay on home (Task 11, spec §15). Home renders the greeting overlay (server side, opaque) plus a hidden,
   viewport-centred copy of the rosette; arrive.js (if it runs) unhides the rosette, restarts the greeting with the
   LOGIN timings measured from the same play() start (name 0.3s-2.2s, rule 2.1s, serial 2.3s, rosette shrinks into the
   header mark slot from 3.3s, lands ~4.4s) and the overlay dissolves 4.4s-4.9s revealing home. The overlay swallows
   clicks (a click skips; it must not also activate the link beneath). CSS-only safety nets: the overlay dissolves
   by 4.9s and the rosette is gone by 5.4s even if the script dies part-way. */
.rosette.rosette-replay{position:fixed;top:50%;left:50%;margin:calc(var(--ros) / -2) 0 0 calc(var(--ros) / -2);z-index:6;opacity:.95;
  animation:ros-appear .5s ease-out backwards,ros-gone .01s 5.4s forwards;}
@keyframes ros-appear{from{opacity:0;}}
@keyframes ros-gone{to{opacity:0;visibility:hidden;}}
.rosette-replay .ring-a{animation:ros-spin 90s linear infinite;}
.rosette-replay .ring-b{animation:ros-spin 70s linear infinite reverse;}
.rosette-replay .crest path,.rosette-replay .crest line{animation:none;}
.greeting.overlay.replay{pointer-events:auto;}
.greeting.overlay.replay.play{animation:g-out .5s 4.4s forwards;}
/* The engraving is over home's cards once the overlay dissolves, so unlike the login path (which navigates away) the
   text fades out first (4.1s-4.4s), then the background dissolves (4.4s-4.9s). */
.greeting.overlay.replay.play .greeting-welcome{animation:g-fade .6s 0s forwards,g-text-out .3s 4.1s forwards;}
.greeting.overlay.replay.play .greeting-name{animation:g-sweep 1.9s .3s cubic-bezier(.45,0,.2,1) forwards,g-text-out .3s 4.1s forwards;}
.greeting.overlay.replay.play .greeting-rule{animation:g-rule .8s 2.1s forwards,g-text-out .3s 4.1s forwards;}
.greeting.overlay.replay.play .greeting-serial{animation:g-fade .8s 2.3s forwards,g-text-out .3s 4.1s forwards;}
@keyframes g-text-out{to{opacity:0;}}

@media (max-width:999px){
  .rosette{--ros:300px;}   /* tablet: keeps clear of the 380px key plate down to ~720px */
}
@media (max-width:720px){
  /* Phones: smaller and UP, centred behind the header/headline area so its acorn never sits behind the key plate. */
  .rosette{--ros:240px;opacity:.4;}
  body.landing .rosette{top:93px;right:calc(var(--ros) * -.3);}   /* just under the header rule, centre ~48px in from the right edge */
  .hero{padding-top:40px;}
  .key-plate{max-width:none;}
  .key-go{min-height:40px;}
  dialog.disclosure{margin:auto 8px 8px;max-width:none;width:calc(100vw - 16px);}
  .disc-scroll{max-height:calc(100vh - 100px);max-height:calc(100dvh - 100px);}
  .disc-row{grid-template-columns:1fr;gap:2px;}
  /* The 300px rosette sits behind the name and serial line once it has glided to centre: dim it so the
     engraving stays legible, then bring it back up as it shrinks into the header (same clock as crest-cycle). */
  .rosette.is-greeting svg{animation:ros-dim 4.6s linear forwards;}
  /* "API DISCLOSURE" is a plain header link, not one of the stacked menu rows: no row underline, and
     a 34px box (the height of the logged-in menu button) so the wordmark sits at the same y as on home. */
  .sitehead-landing .navlink{border-bottom:0;min-height:34px;padding:0;}
}
@keyframes ros-dim{0%{opacity:1;}6.5%,71.7%{opacity:.22;}84.8%,100%{opacity:1;}}
@media (prefers-reduced-motion:reduce){
  .hero>*{animation:none;}
  .rosette .ring,.rosette .crest path,.rosette .crest line{animation:none;}
  .greeting.play .greeting-welcome,.greeting.play .greeting-rule,.greeting.play .greeting-serial,
  .greeting.overlay.play .greeting-welcome,.greeting.overlay.play .greeting-rule,.greeting.overlay.play .greeting-serial{animation:none;opacity:1;transform:none;}
  .greeting.play .greeting-name,.greeting.overlay.play .greeting-name{animation:none;clip-path:none;}
  /* spec §9: a static "Welcome back" for about 1.2s (0.8s hold + 0.4s fade). */
  .greeting.overlay.play{animation:g-out .4s .8s forwards;}
}
