/* Shared, short settling motion. Visibility, focus and actions stay native. */
:root { --view-ease: cubic-bezier(.4, 0, .2, 1); }

/* Moving to another page: where the browser can animate between pages, the page
   being left stays on screen until the next one has drawn, and the next one
   dissolves in over it. A page that needs a moment to show its content
   (data-hold-arrival) stays transparent until it does (motion.js releaseArrival),
   for at most one second, so the screen never empties in between. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
::view-transition-old(root) { animation: none; mix-blend-mode: normal; }
::view-transition-new(root) { animation: page-in 200ms var(--view-ease) both; mix-blend-mode: normal; }
:root[data-hold-arrival]:not([data-arrived])::view-transition-new(root) { animation: page-held-in 1200ms var(--view-ease) both; }
@keyframes page-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes page-held-in {
  0%, 75% { opacity: 0; }
  90%, to { opacity: 1; }
}
/* The sheet a tapped house card opens into (motion.js leavePage). It is part of
   the page being left, so between pages it stays on screen, full size, until the
   punch list has faded in over it. (A separately named sheet rendered as a black
   frame in Safari.) */
.page-opening { position: fixed; z-index: 2147483647; pointer-events: none; }
/* Views arrive from the dimmed state the previous view left in (motion.js
   leavePage), never from an empty screen, so moving between pages doesn't flash. */
@keyframes view-forward {
  from { opacity: .6; transform: translateX(16px); }
  to { opacity: 1; transform: none; }
}
@keyframes view-back {
  from { opacity: .6; transform: translateX(-16px); }
  to { opacity: 1; transform: none; }
}
@keyframes view-settle {
  from { opacity: .6; }
  to { opacity: 1; }
}
@keyframes panel-reveal {
  from { opacity: .35; transform: translateY(5px); }
  to { opacity: 1; transform: none; }
}
@keyframes menu-reveal {
  from { transform: translateY(-4px); }
  to { transform: none; }
}
@keyframes dialog-content-reveal {
  from { opacity: .45; }
  to { opacity: 1; }
}
@keyframes dialog-reveal {
  from { opacity: 1; transform: translateY(16px) scale(.97); }
  to { opacity: 1; transform: none; }
}


/* Sliding views never widen the page into a sideways scroll; clip keeps sticky headers working. */
html, body { overflow-x: clip; }
/* These persistent surfaces animate only when revealed, never on polling. */
#project-detail:not([hidden]),
#crew-activity:not([hidden]) { animation: view-forward 300ms var(--view-ease); }
/* The punch list only fades: a moving #app would carry the fixed Camera/Note
   dock off the bottom of the page until the motion ended, then pop it in. */
#app.arriving:not([hidden]) { animation: view-settle 300ms var(--view-ease); }
#crew-houses-panel:not([hidden]) { animation: view-back 300ms var(--view-ease); }
/* The Builder's tabs switch at once, tapped or swiped, every tab alike. Going back
   to a tab (from a project's page, or arriving back from a punch list) slides it
   back in, and a crew's week opens forward and closes back (builder.js playView). */
.view-forward:not([hidden]) { animation: view-forward 300ms var(--view-ease); }
.view-back:not([hidden]) { animation: view-back 300ms var(--view-ease); }
#list-search-region:not([hidden]),
#crew-view-form:not([hidden]) { animation: panel-reveal 300ms var(--view-ease); }
.punch-more-menu:not([hidden]),
.detail-more-menu:not([hidden]) {
  transform-origin: top right;
  animation: menu-reveal 220ms var(--view-ease);
}
.punch-more-menu:not([hidden]) > *,
.detail-more-menu:not([hidden]) > * { animation: dialog-content-reveal 220ms ease-out; }
/* A card that opens another screen answers the finger the moment it lands, so
   the tap is never met by a still screen while the next one loads. */
@media (hover: none) and (prefers-reduced-motion: no-preference) {
  #portfolio-view .project-card, #crew-app .project-card, .feed-card { transition: transform 120ms ease-out; }
  #portfolio-view .project-card:active:not(:has(button:active)),
  #crew-app .project-card:has(> .project-card-link:active),
  .feed-card:has(.feed-card-title a:active) { transform: scale(.985); }
}
.list-filter button, .trade-filter button, .crew-tabs button {
  transition: background-color 200ms ease-out, border-color 200ms ease-out, color 200ms ease-out;
}
.document-summary-chevron { transition: transform 300ms var(--view-ease); }
.document-details[data-motion-expanded="false"] > .document-summary > .document-summary-chevron { transform: none; }
.list-section-chevron { transition: transform 300ms var(--view-ease); }

/* CSS-only fallback for surfaces without a source control. */
dialog[open] { animation: dialog-reveal 340ms var(--view-ease); }
dialog[open]:is(.markup-dialog, .document-viewer) { animation: dialog-content-reveal 180ms ease-out; }
dialog:not([open]), dialog:not([open])::backdrop { pointer-events: none; }
/* A quick page load never flashes the loading card; a slow one fades it in. */
#loading.center-card { animation: loading-appear 260ms ease-out 450ms both; }
/* The same for the Crew hub's loading card and the Builder's loading screen,
   whose plain surface stays while only its message waits to fade in. */
main#loading.gate-card, div#loading.gate-card > * { animation: loading-appear 260ms ease-out 450ms both; }
@keyframes loading-appear { from { opacity: 0; } to { opacity: 1; } }
/* Toasts lifted above an open dialog keep their normal corner placement. */
.toast[popover] { top: auto; left: auto; width: auto; height: auto; margin: 0; border: 0; overflow: visible; }

/* motion.js slides, raises or reveals the surface with Web Animations; Safari
   does not need discrete display/overlay transitions to animate native dialogs. */
dialog[data-motion-dialog] { animation: none !important; transition: none; transform-origin: center bottom; }
dialog[data-motion-dialog][open] { opacity: 1; transform: none; }
dialog[data-motion-dialog] > * { animation: none !important; }
dialog[data-motion-dialog]::backdrop { transition: none; animation: backdrop-in 360ms ease-out both; }
/* A sheet opened from inside an item (trade, blocker, review, archive) takes
   the item's place as the one open pop-up, and a still copy of the item stays
   beneath it (app.js showDetailUnderlay): the sheet dims and covers the item
   like any sheet over a page, and the item comes straight back when it closes. */
dialog[data-motion-instant]::backdrop { animation: none !important; }
.detail-underlay { position: fixed !important; z-index: 2147483000; margin: 0 !important; overflow: hidden; background: var(--paper); pointer-events: none; }
/* The dimmed page clears together with the surface instead of snapping off afterwards. */
dialog[data-motion-closing]::backdrop { animation: backdrop-out 300ms ease-in both; }
.punch-more-menu:not([hidden]), .detail-more-menu:not([hidden]),
.punch-more-menu:not([hidden]) > *, .detail-more-menu:not([hidden]) > * { animation: none; }
@media (max-width: 650px), (pointer: coarse) and (max-height: 650px) {
  /* Cover the area below a keyboard-sized sheet, including translucent iOS
     keyboard chrome. Reveal the list only while entering or leaving. */
  dialog:is(.add-dialog, .detail-dialog)::backdrop { background: var(--card); backdrop-filter: none; }
  dialog:is(.add-dialog, .detail-dialog)[data-motion-dialog]::backdrop { animation: sheet-backdrop-in 440ms var(--view-ease) both; }
  dialog:is(.add-dialog, .detail-dialog)[data-motion-closing]::backdrop { animation: sheet-backdrop-out 360ms var(--view-ease) both; }
}
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes backdrop-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes sheet-backdrop-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheet-backdrop-out { from { opacity: 1; } to { opacity: 0; } }

/* Loading screens: the app icon's three lines draw in and each one gets
   checked off, every 2.4 seconds. The loop starts partway in, so even a quick
   open shows a check. It stands still, every line checked, with Reduce Motion
   and once loading has failed. */
.loading-checklist { display: block; width: 88px; height: auto; margin: 0 auto 16px; }
.loading-checklist-tile { fill: var(--accent, #c83e49); }
.loading-checklist-bars { fill: #fff; }
.loading-checklist-checks { fill: none; stroke: #fff; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.loading-checklist-bar, .loading-checklist-check { transform-box: fill-box; }
.loading-checklist-bar { transform-origin: left center; animation: loading-checklist-bar 2.4s cubic-bezier(.2, 0, 0, 1) -.9s infinite; }
.loading-checklist-check { transform-origin: center; animation: loading-checklist-check 2.4s cubic-bezier(.3, 1.6, .5, 1) -.9s infinite; }
.loading-checklist :nth-child(2) { animation-delay: -.74s; }
.loading-checklist :nth-child(3) { animation-delay: -.58s; }
#loading.is-stopped .loading-checklist *, #loading:has(#boot-retry:not([hidden])) .loading-checklist * { animation: none; }
@keyframes loading-checklist-bar {
  0% { transform: scaleX(0); opacity: 1; }
  18%, 82% { transform: scaleX(1); opacity: 1; }
  94%, 100% { transform: scaleX(1); opacity: 0; }
}
@keyframes loading-checklist-check {
  0%, 30% { transform: scale(0); opacity: 0; }
  42%, 82% { transform: scale(1); opacity: 1; }
  94%, 100% { transform: scale(1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after, *::backdrop {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}
@media print {
  *, *::before, *::after, *::backdrop { animation: none !important; transition: none !important; }
}
