/* shell.css — the parts of the signed-in shell that must be IDENTICAL on every
   page: the masthead, and the page-to-page slide.

   Before this file each page carried its own masthead CSS and they had drifted:
   the wordmark was 1.3rem on home, 1.16rem on account and activity, 1.14rem on
   the resume, and application.html had a fourth size of its own. Two of the four
   made the wordmark clickable, two did not. One file now owns all of it.

   It deliberately declares NO theme tokens. Every page already defines its own
   --paper/--ink/--crimson set, and this file only consumes them, so it can be
   dropped into any page without changing that page's palette. */

/* ── the masthead ────────────────────────────────────────────────────────── */
.mast{position:sticky;top:0;z-index:100;background:var(--paper);border-bottom:1px solid var(--ink);
  display:flex;align-items:center;gap:14px;padding:14px clamp(16px,3vw,32px)}
/* The face is HARDCODED, not read from a token. This is why the wordmark looked
   different from page to page: home resolved --word to Libre Caslon Display,
   account/activity/resume resolved it to Libre Caslon TEXT, and application.html
   loaded neither and fell back to Georgia. Display is the canon (unified
   2026-06-22), so the masthead names it directly and no page can drift again. */
.mast .wordmark{
  font-family:'Libre Caslon Display',Georgia,serif;
  font-size:1.3rem;line-height:1;color:var(--ink);cursor:pointer;
  background:none;border:none;padding:0;letter-spacing:0;
  transition:opacity .22s var(--ease,cubic-bezier(.16,1,.3,1))}
.mast .wordmark b{color:var(--crimson);font-weight:400}
.mast .wordmark:hover{opacity:.62}
.mast .crumb{font-family:var(--sans,'Inter',system-ui,sans-serif);font-weight:600;font-size:11px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);border-left:1px solid var(--hair);padding-left:14px}
.mast .grow{flex:1}
.mast .whoami{font-family:var(--sans,'Inter',system-ui,sans-serif);font-size:11px;letter-spacing:.04em;color:var(--muted);
  max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mast .whoami b{color:var(--ink);font-weight:500}
.mast .tt{width:32px;height:30px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--hair);color:var(--muted);background:none;cursor:pointer;
  transition:color .2s var(--ease,cubic-bezier(.16,1,.3,1)),border-color .2s var(--ease,cubic-bezier(.16,1,.3,1))}
.mast .tt:hover{color:var(--ink);border-color:var(--ink)}
.mast .tt svg{width:16px;height:16px;display:block}
.mast .gbtn{font-family:var(--sans,'Inter',system-ui,sans-serif);font-weight:600;font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--hair);background:none;padding:7px 11px;cursor:pointer;
  transition:color .2s var(--ease,cubic-bezier(.16,1,.3,1)),border-color .2s var(--ease,cubic-bezier(.16,1,.3,1))}
.mast .gbtn:hover{color:var(--ink);border-color:var(--ink)}
.mast .gbtn.fill{background:var(--crimson);color:var(--on-crimson);border-color:var(--crimson)}
@media(max-width:620px){.mast .crumb,.mast .whoami{display:none}}


/* ══ THE PAGE SLIDE ═══════════════════════════════════════════════════════════
   REWRITTEN 2026-07-19, because the hand-rolled version was visibly broken.

   The old approach could not work. These are separate documents, so it animated
   the outgoing page, THEN called location.href, THEN waited for the next page to
   be fetched, parsed and painted, THEN animated it in. The gap in the middle is
   however long the navigation takes, which is exactly the stall the founder saw:
   the slide would run, stop dead, and the new page would arrive late and start a
   second, unrelated slide.

   Cross-document View Transitions fix it at the root. The browser holds a
   snapshot of the outgoing page on screen while the next document loads, and
   only then runs ONE animation between the two. There is no gap to see, because
   there is no moment where neither page is on screen.

   Both documents must opt in, which is why every shell page loads this file. */
@view-transition{ navigation:auto }

/* Only navigations we labelled get the slide. The scope matters: several pages
   already use document.startViewTransition for the theme toggle's circular
   wipe, and unscoped ::view-transition rules would hijack that animation too. */
html[data-nav]::view-transition-group(root){
  animation-duration:.5s;
  animation-timing-function:cubic-bezier(.37,0,.63,1);   /* ease-in-out-sine */
}
/* the snapshots must not cross-fade underneath the slide, or the whole thing
   reads as a dissolve with movement rather than as one sheet moving over another */
html[data-nav]::view-transition-old(root),
html[data-nav]::view-transition-new(root){
  mix-blend-mode:normal;
  animation-duration:.5s;
  animation-timing-function:cubic-bezier(.37,0,.63,1);
  animation-fill-mode:both;
}

/* FORWARD · home -> child. Home slides left and dims as the base layer; the
   child comes in from the right, over it, carrying the shadow. */
html[data-nav="fwd"]::view-transition-old(root){ animation-name:pg-out-left; z-index:1 }
html[data-nav="fwd"]::view-transition-new(root){ animation-name:pg-in-right; z-index:2 }

/* BACK · child -> home. The child slides off to the right, still on top; home
   returns from the left with no shadow, because home is the base and never sits
   over anything. */
html[data-nav="back"]::view-transition-old(root){ animation-name:pg-out-right; z-index:2 }
html[data-nav="back"]::view-transition-new(root){ animation-name:pg-in-left;  z-index:1 }

@keyframes pg-in-right{
  from{ transform:translateX(100%); box-shadow:-24px 0 60px -12px rgba(0,0,0,.42) }
  to  { transform:translateX(0);    box-shadow:-24px 0 60px -12px rgba(0,0,0,0) }
}
@keyframes pg-out-left{
  from{ transform:translateX(0);      filter:brightness(1) }
  to  { transform:translateX(-22%);   filter:brightness(.86) }
}
@keyframes pg-out-right{
  from{ transform:translateX(0);    box-shadow:-24px 0 60px -12px rgba(0,0,0,.42) }
  to  { transform:translateX(100%); box-shadow:-24px 0 60px -12px rgba(0,0,0,.42) }
}
@keyframes pg-in-left{
  from{ transform:translateX(-22%); filter:brightness(.86) }
  to  { transform:translateX(0);    filter:brightness(1) }
}

/* the sheets slide past the viewport edge; never let that open a scrollbar */
html[data-nav],html[data-nav] body{ overflow-x:hidden }

@media(prefers-reduced-motion:reduce){
  html[data-nav]::view-transition-old(root),
  html[data-nav]::view-transition-new(root){ animation:none }
}


/* ══ THE CARD SYSTEM ══════════════════════════════════════════════════════════
   Added 2026-07-19, applied site-wide.

   A card is MATERIAL: a sheet of a different tone, lifted off the page by a
   soft shadow, not ruled off it by a hairline. Every page defines --card and
   --card-sh for its three themes; this file owns the behaviour so a card looks
   and behaves identically wherever it appears.

   The selector list names the app's card classes deliberately. One definition
   in one file beats the same rules copied into six <style> blocks, which is
   exactly how the masthead drifted. New cards should just use .acard. */
.acard, .plate, .nav-plate, .tile, .sblock, .ev .card, .threads, .chat, .ecard{
  border:none;
  background:var(--card, var(--paper));
  box-shadow:var(--card-sh, 0 1px 2px rgba(0,0,0,.05), 0 6px 18px rgba(0,0,0,.07));
  position:relative;
}

/* THE HOVER OUTLINE — a line drawn around the card, corner to corner.
   Done entirely with two pseudo-elements and four gradient slivers, so it needs
   NO extra markup and works on cards that are generated at runtime. Each edge
   is a 1px gradient grown from zero along one axis; the anchor points chain the
   four edges into one continuous stroke: top runs left to right, right runs
   down, bottom runs right to left, left runs back up. */
.acard::before,.acard::after,.plate::before,.plate::after,
.nav-plate::before,.nav-plate::after,.tile::before,.tile::after,
.sblock::before,.sblock::after,.ev .card::before,.ev .card::after,
.threads::before,.threads::after,.chat::before,.chat::after,.ecard::before,.ecard::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:3;
}
.acard::before,.plate::before,.nav-plate::before,.tile::before,
.sblock::before,.ev .card::before,.threads::before,.chat::before,.ecard::before{
  background:
    linear-gradient(var(--crimson),var(--crimson)) left top/0% 1px no-repeat,
    linear-gradient(var(--crimson),var(--crimson)) right top/1px 0% no-repeat;
  transition:background-size .34s var(--ease,cubic-bezier(.16,1,.3,1));
}
.acard::after,.plate::after,.nav-plate::after,.tile::after,
.sblock::after,.ev .card::after,.threads::after,.chat::after,.ecard::after{
  background:
    linear-gradient(var(--crimson),var(--crimson)) right bottom/0% 1px no-repeat,
    linear-gradient(var(--crimson),var(--crimson)) left bottom/1px 0% no-repeat;
  transition:background-size .34s var(--ease,cubic-bezier(.16,1,.3,1)) .17s;
}
.acard:hover::before,.plate:hover::before,.nav-plate:hover::before,.tile:hover::before,
.sblock:hover::before,.ev .card:hover::before,.threads:hover::before,.chat:hover::before,
.acard:hover::after,.plate:hover::after,.nav-plate:hover::after,.tile:hover::after,
.sblock:hover::after,.ev .card:hover::after,.threads:hover::after,.chat:hover::after,.ecard:hover::before,.ecard:hover::after{
  background-size:100% 1px, 1px 100%;
}

@media(prefers-reduced-motion:reduce){
  .acard::before,.acard::after,.plate::before,.plate::after,
  .nav-plate::before,.nav-plate::after,.tile::before,.tile::after,
  .sblock::before,.sblock::after,.ev .card::before,.ev .card::after,
  .threads::before,.threads::after,.chat::before,.chat::after,.ecard::before,.ecard::after{transition:none}
}
