/* HRMXN AppShell — the shell's own styles: the side panel and the moves between views.
   Built only from the design-system tokens (style.css). An app changes the defaults below in its own CSS, e.g.
     :root { --hx-side-font: 14px; }
   and keeps everything else, so every hrmxn app looks and moves the same. */
:root {
	--hx-side-font: 15px;              /* side panel rows */
	--hx-side-pad-l: var(--s8);        /* side panel inner space, wide screens (phones keep the design-system spacing) */
	--hx-side-pad-r: var(--s6);
	--hx-tile-in: 340ms;               /* a new tile swinging in */
	--hx-tile-out: 230ms;              /* an old tile swinging out */
	--hx-flight: 420ms;                /* the cover flying between a tile and its item page */
}

/* ── Side panel: one line per row; count and the current-entry square on the right ── */
.side .contents--sm a { grid-template-columns: minmax(0, 1fr) auto auto; column-gap: var(--s2); }   /* the shell's list (no numbers) */
.side .contents--sm a > span:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contents a .hx-count { color: var(--faint); font-size: 12px; font-variant-numeric: tabular-nums; }
.side .contents--sm a { font-size: var(--hx-side-font); }
/* the panel heading (a long name) stays on one line too */
.side .side-group > .label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* the way back up */
.hx-back { display: inline-block; margin-bottom: var(--s3); color: var(--muted); text-decoration: none; }
.hx-back:hover { color: var(--ink); }
@media (min-width: 861px) {
	.page--app .side { padding-left: var(--hx-side-pad-l); padding-right: var(--hx-side-pad-r); }
}
/* the current row morphs plain <-> bold (Archivo is a variable font, so the weight can glide); the red square fades */
.side .contents a { transition: font-weight 260ms ease; }   /* weight only: a fading colour flickers on the clicked (hovered) row */
.side .contents a::after { content: ""; width: 8px; height: 8px; background: var(--accent); opacity: 0; transition: opacity 260ms ease; }
.side .contents a[aria-current]::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .side .contents a, .side .contents a::after { transition: none; } }
/* a new level of the side panel slides in: from the right when going deeper (.hx-side-in), from the left when going
   back out (.hx-side-out); the old level slides away the other way (html[data-hx-side], below). shell.js. */
#hx-side.hx-side-in > * { animation: 260ms cubic-bezier(.1, .9, .2, 1) 60ms both hx-slide-from-right; }
#hx-side.hx-side-out > * { animation: 260ms cubic-bezier(.1, .9, .2, 1) 60ms both hx-slide-from-left; }
@keyframes hx-slide-from-right { from { opacity: 0; transform: translateX(28px); } }
@keyframes hx-slide-from-left { from { opacity: 0; transform: translateX(-28px); } }
@keyframes hx-slide-to-left { to { opacity: 0; transform: translateX(-28px); } }
@keyframes hx-slide-to-right { to { opacity: 0; transform: translateX(28px); } }
@media (prefers-reduced-motion: reduce) { #hx-side > * { animation: none !important; } }

/* ── Moves between views, Windows-Phone style (same-document View Transitions: Chrome/Edge 126+, Safari 18.2+) ──
   Old and new view OVERLAP, so the screen is never empty (shell.js):
   - each tile that was on screen becomes its own picture, "hx-t-<delay>-<n>", and swings out around its left edge
     in a wave, while the new view's tiles already swing in (live, class .hx-in);
   - open an item: its cover flies to the item page (hx-cover), the details fade in; back: the cover flies home;
   - top bar and footer stay still; the side panel swaps in one step (its rows glide plain <-> bold).
   html[data-hx-nav] says which move runs: page (one list to another), open, back, fade (to a view of another kind). */
.page--app .head { view-transition-name: hx-head; }
.page--app .foot { view-transition-name: hx-foot; }
.page--app .side { view-transition-name: hx-side; }
.page--app .main { view-transition-name: hx-main; }
.page--app #hx-side { view-transition-name: hx-side-list; }   /* the panel's content, apart from its frame */

/* every old tile picture (and anything else not named below): the turnstile out; delays set by shell.js */
::view-transition-old(*) { animation: var(--hx-tile-out) cubic-bezier(.6, 0, .9, .4) both hx-tile-out; transform-origin: left center; }
::view-transition-new(*) { animation: none; }

::view-transition-old(root), ::view-transition-new(root),
::view-transition-new(hx-head), ::view-transition-new(hx-foot) { animation: none; }
/* the bars do not change from one view to the next: only their live new picture is shown, so a menu closing in the
   top bar, or a tab mark gliding in the footer, keeps moving during the move */
::view-transition-old(hx-head), ::view-transition-old(hx-foot) { animation: none; opacity: 0; }
::view-transition-old(hx-side), ::view-transition-old(hx-side-list) { animation: none; opacity: 0; }
::view-transition-new(hx-side), ::view-transition-new(hx-side-list) { animation: none; }
/* a new level: the old one slides away (the new one slides in, live: #hx-side.hx-side-in / -out above) */
html[data-hx-side="in"]::view-transition-old(hx-side-list) { opacity: 1; animation: 160ms ease-in both hx-slide-to-left; }
html[data-hx-side="out"]::view-transition-old(hx-side-list) { opacity: 1; animation: 160ms ease-in both hx-slide-to-right; }
/* the rest of the old content (title, text): fades while the new one comes in */
::view-transition-old(hx-main) { animation: 200ms ease-out both hx-out; transform-origin: initial; }
::view-transition-new(hx-main) { animation: 380ms cubic-bezier(.1, .9, .2, 1) both hx-turn-in; transform-origin: left center; }
html[data-hx-nav="back"]::view-transition-new(hx-main),
html[data-hx-nav="page"]::view-transition-new(hx-main) { animation: none; } /* there, tiles and title swing in by themselves */
::view-transition-group(hx-cover) { animation-duration: var(--hx-flight); animation-timing-function: cubic-bezier(.1, .9, .2, 1); z-index: 10; }
/* stacking: the flying cover above everything; top bar, footer and side panel above the swinging tiles */
::view-transition-group(hx-head), ::view-transition-group(hx-foot), ::view-transition-group(hx-side),
::view-transition-group(hx-side-list) { z-index: 5; }
/* one list to another: the old content that is not a tile (cut-off tiles, text) goes quickly, before the new title
   swings in where it was */
html[data-hx-nav="page"]::view-transition-old(hx-main) { animation: 120ms linear both hx-out; }
::view-transition-old(hx-cover) { animation: var(--hx-flight) ease-out both hx-out; transform-origin: initial; }
::view-transition-new(hx-cover) { animation: none; }

@keyframes hx-out { to { opacity: 0; } }
@keyframes hx-in { from { opacity: 0; } }
@keyframes hx-turn-in { from { opacity: 0; transform: perspective(1400px) rotateY(-55deg) translateX(32px); } }
@keyframes hx-tile-out { to { opacity: 0; transform: perspective(800px) rotateY(80deg); } }
@keyframes hx-tile-in { from { opacity: 0; transform: perspective(800px) rotateY(-80deg); } }

/* the new view's tiles swing in (live), starting just after the old tiles begin to leave */
.hx-in {
	animation: hx-tile-in var(--hx-tile-in) cubic-bezier(.1, .9, .2, 1) both;
	animation-delay: var(--d, 70ms);
	transform-origin: left center;
}
html[data-hx-nav="back"] .pagehead { animation: 340ms cubic-bezier(.1, .9, .2, 1) 40ms both hx-turn-in; transform-origin: left center; }
/* one list to another: only what changes swings in (data-hx-swing: title, crumbs, count …) ... */
html[data-hx-nav="page"] [data-hx-swing]:not(.is-kept) { animation: 340ms cubic-bezier(.1, .9, .2, 1) 40ms both hx-turn-in; transform-origin: left center; }
/* ... what is the same in both views stays put (data-hx-keep; shell.js names it "hx-keep-<name>" in both views):
   the new one is shown at once; if its text changes (a page number) the old one fades off it; if it moves (a line
   more or less above it) it slides there. */
.is-kept[data-hx-keep] { view-transition-class: hx-keep; }
::view-transition-old(*.hx-keep) { animation: 120ms linear both hx-out; transform-origin: initial; }
::view-transition-new(*.hx-keep) { animation: none; }
::view-transition-group(*.hx-keep) { animation-duration: 300ms; animation-timing-function: cubic-bezier(.1, .9, .2, 1); }

/* opening an item, or to a view of another kind: the old content fades out at once, the new one fades in */
html[data-hx-nav="open"]::view-transition-old(hx-main),
html[data-hx-nav="fade"]::view-transition-old(hx-main) { animation: 90ms linear both hx-out; }
html[data-hx-nav="open"]::view-transition-new(hx-main),
html[data-hx-nav="fade"]::view-transition-new(hx-main) { animation: 260ms ease-out 120ms both hx-in; transform-origin: initial; }

/* a change inside the view shown (AppShell.morph, e.g. grid <-> list): the old look fades into the new one */
html[data-hx-nav="morph"]::view-transition-old(hx-main) { animation: 140ms ease-out both hx-out; transform-origin: initial; }
html[data-hx-nav="morph"]::view-transition-new(hx-main) { animation: 220ms ease-out 60ms both hx-in; transform-origin: initial; }

/* the colour theme changed (theme.js): everything crossfades from the old colours to the new ones, nothing moves */
html[data-hx-nav="theme"]::view-transition-group(*) { animation: none; }
html[data-hx-nav="theme"]::view-transition-old(*) { animation: 260ms ease both hx-out; opacity: 1; transform-origin: initial; }
html[data-hx-nav="theme"]::view-transition-new(*) { animation: 260ms ease both hx-in; transform-origin: initial; }

/* waiting for a view (slower than 150 ms): a thin line runs along the bottom of the top bar */
.page--app .head { position: relative; }
html[data-hx-loading] .page--app .head::after {
	content: ""; position: absolute; left: 0; bottom: -1px; width: 30%; height: 2px; background: var(--ink);
	animation: hx-loading 900ms ease-in-out infinite;
}
@keyframes hx-loading { from { transform: translateX(-100%); } to { transform: translateX(340%); } }

/* opening the app (first paint): the side panel and the content fade in once */
html.hx-enter #hx-main > *, html.hx-enter #hx-side > * { animation: hx-in 280ms ease-out both; }

/* ── Footer tabs (optional: AppShell(..., tabs=[...]); no tabs = no footer) ──
   the current tab: ink colour and a red mark on its top edge that grows out from the middle */
.foot.hx-foot { justify-content: center; padding: 0; }
.hx-tabs { display: flex; align-self: stretch; gap: 0; }
.foot .hx-tabs a { position: relative; display: flex; align-items: center; gap: var(--s2); padding: 0 var(--s6);
	color: var(--muted); transition: color 200ms ease; }
.foot .hx-tabs a:hover, .foot .hx-tabs a[aria-current] { color: var(--ink); }
.hx-tabs a::before { content: ""; position: absolute; left: var(--s4); right: var(--s4); top: -1px; height: 2px;
	background: var(--accent); transform: scaleX(0); transition: transform 240ms cubic-bezier(.1, .9, .2, 1); }
.hx-tabs a[aria-current]::before { transform: scaleX(1); }
.hx-tabs .i { width: 18px; height: 18px; }

@media (prefers-reduced-motion: reduce) {
	::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
	html[data-hx-loading] .page--app .head::after { animation: none; width: 100%; opacity: .4; }
	.hx-tabs a, .hx-tabs a::before { transition: none; }
	.hx-in, html[data-hx-nav] .pagehead, html[data-hx-nav] .pagehead * { animation: none !important; }
}
