/* Loading, the arrival flash, error and no-script states. */

/* ---- Loading ----------------------------------------------------------- */

.loading {
  position: absolute;
  z-index: 5;
  top: 50%;
  left: 50%;
  text-align: center;
  pointer-events: none; /* never in the way of the model, even while fading out */
  transform: translate(-50%, -50%);
  transition: opacity var(--dur-4) var(--ease-exit), visibility 0s var(--dur-4);
}

[data-state="ready"] .loading,
[data-state="error"] .loading {
  opacity: 0;
  visibility: hidden;
}

.loading__text {
  margin: 0 0 14px;
  color: var(--ink-60);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.loading__track {
  width: 168px;
  height: 2px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 2px;
  background: var(--hairline-strong);
}

.loading__bar {
  height: 100%;
  background: var(--amber);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-2) var(--ease-estate);
}

.loading__track.is-indeterminate .loading__bar {
  width: 40%;
  animation: loading-slide 1.5s var(--ease-estate) infinite;
}

@keyframes loading-slide {
  from { transform: translateX(-110%); }
  to { transform: translateX(260%); }
}

/* ---- Poster ------------------------------------------------------------
   A still of the estate at about the size the model will take. While the
   model downloads it is a soft, faint presence behind the progress; it clears
   as the model arrives. If the browser cannot draw 3D, the same file is the
   map (see "No 3D" below). */

.poster {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: min(88.5vw, 135vh); /* the slab is 83% of the file's width */
  height: auto;
  opacity: 0.5;
  filter: blur(12px) saturate(0.85);
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -50%) scale(0.97);
  transition: opacity var(--dur-5) var(--ease-exit), transform var(--dur-6) var(--ease-estate);
}

[data-state="ready"] .poster {
  opacity: 0;
  transform: translate(-50%, -50%);
}

@media (max-width: 640px) {
  .poster {
    width: 97vw;
  }
}

/* A small phone on its side fits the model to the room right of the column
   (ui.js insets), so the poster and the progress take that room too: the
   poster clears where the model arrives, and neither is under the column as
   it rises in. The room keeps 14px clear of the list (229px wide with the
   names as they are, never over 42vw; places.css), the names switch (36px
   tall) and the view controls (42px, 50px on a touch screen). */
@media (max-width: 640px) and (max-height: 500px) and (orientation: landscape) {
  .poster,
  .loading {
    --room-left: calc(var(--edge) + env(safe-area-inset-left, 0px) + min(229px, 42vw) + 14px);
    --room-top: calc(var(--edge) + env(safe-area-inset-top, 0px) + 36px + 14px);
    --room-bottom: calc(var(--foot-row) + var(--room-controls, 42px) + 14px);
    left: calc(50% + var(--room-left) / 2);
    top: calc(50% + (var(--room-top) - var(--room-bottom)) / 2);
  }

  /* The model is drawn 89% as wide as the room, or 1.67 times as wide as the
     room is tall, whichever is less (measured with the camera's fit as it is
     in config.js). The slab is 83% of the file's width, and its middle 49% of
     the way across and 44% down. */
  .poster {
    width: min(1.075 * (100% - var(--room-left)), 2.02 * (100vh - var(--room-top) - var(--room-bottom)));
    width: min(1.075 * (100% - var(--room-left)), 2.02 * (100dvh - var(--room-top) - var(--room-bottom)));
    transform: translate(-48.8%, -44%) scale(0.97);
  }

  [data-state="ready"] .poster {
    transform: translate(-48.8%, -44%);
  }
}

@media (max-width: 640px) and (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  .poster,
  .loading {
    --room-controls: 50px;
  }
}

/* Narrower than 480px (only a desktop zoomed right in) that room is too narrow
   for the progress, which stays in the middle of the window. */
@media (max-width: 479px) and (max-height: 500px) and (orientation: landscape) {
  .loading {
    top: 50%;
    left: 50%;
  }
}

/* ---- The arrival flash ------------------------------------------------
   Sunstone's house light is raking light (brand/IDENTITY.md, sections 5.2 and
   7): one band at 30 degrees, rising to the right. Here it crosses the page
   once as the model arrives, then ui.js takes the element away. Overlay, so it
   lands on the model and barely touches the paper. */

.flash {
  position: absolute;
  z-index: 3;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  mix-blend-mode: overlay;
}

.flash::before {
  content: "";
  position: absolute;
  inset: -70% -90%;
  opacity: 0;
  background: linear-gradient(150deg,
    rgb(200 133 63 / 0) 43%,
    rgb(246 222 186 / 0.3) 47.5%,
    rgb(255 240 214 / 0.46) 50%,
    rgb(246 222 186 / 0.3) 52.5%,
    rgb(200 133 63 / 0) 57%);
  transform: translate3d(-30%, 0, 0);
}

[data-state="ready"] .flash::before {
  animation: flash 1800ms var(--ease-estate) 420ms 1 both;
}

@keyframes flash {
  0% { opacity: 0; transform: translate3d(-30%, 0, 0); }
  16% { opacity: 1; }
  84% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(30%, 0, 0); }
}

.flash[hidden] {
  display: none;
}

@media (prefers-reduced-transparency: reduce) {
  .flash {
    display: none;
  }
}

/* ---- Error: a double bezel ---------------------------------------------- */

.error {
  position: absolute;
  z-index: 6;
  top: 50%;
  left: 50%;
  box-sizing: border-box;
  width: min(460px, calc(100vw - 2 * var(--edge)));
  transform: translate(-50%, -50%);
}

.error[hidden] {
  display: none;
}

.error__card {
  box-sizing: border-box;
  padding: 6px;
  border-radius: var(--radius-shell);
  background: var(--bezel-shell);
  box-shadow: var(--bezel-ring);
}

.error__still {
  display: none;
}

.error__core {
  padding: 30px 30px 26px;
  border-radius: var(--radius-core);
  background: var(--bone);
  box-shadow: var(--bezel-core-shadow);
}

.error__title {
  margin: 0 0 10px;
  font-size: 19px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.005em;
}

.error__text {
  margin: 0;
  color: var(--ink-70);
  font-size: 15px;
  line-height: 1.6;
  text-wrap: pretty;
}

.error__detail {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  color: var(--ink-60);
  font: 11px/1.6 var(--font-mono);
  overflow-wrap: anywhere;
}

.error__detail:empty {
  display: none;
}

/* ---- No 3D: the still is the map ----------------------------------------
   main.js sets data-fallback when WebGL is missing. The still and a quieter
   message share the room between the masthead and the foot: the message keeps
   its height, the still takes the rest. Side by side on a phone held sideways. */

[data-fallback="poster"] .poster {
  display: none;
}

[data-fallback="poster"] .error {
  top: calc(var(--edge) + 84px + env(safe-area-inset-top, 0px));
  right: calc(var(--edge) + env(safe-area-inset-right, 0px));
  bottom: var(--foot-row);
  left: calc(var(--edge) + env(safe-area-inset-left, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  width: auto;
  transform: none;
}

/* Its own height until room runs out, then it shrinks: the pair stays together. */
[data-fallback="poster"] .error__still {
  display: block;
  flex: 0 1 auto;
  width: 100%;
  max-width: 1400px; /* the file's own width */
  min-height: 0;
  object-fit: contain;
}

[data-fallback="poster"] .error__card {
  flex: none;
  width: min(460px, 100%);
}

[data-fallback="poster"] .error__core {
  padding: 20px 22px 18px;
}

[data-fallback="poster"] .error__title {
  margin-bottom: 8px;
  font-size: 16px;
}

[data-fallback="poster"] .error__text {
  font-size: 14px;
  line-height: 1.5;
}

[data-fallback="poster"] .error__detail {
  margin-top: 12px;
  padding-top: 10px;
}

@media (max-height: 500px) and (orientation: landscape) {
  [data-fallback="poster"] .error {
    top: calc(var(--edge) + 36px + env(safe-area-inset-top, 0px));
    flex-direction: row;
  }

  [data-fallback="poster"] .error__still {
    width: auto;
    height: 100%;
    min-width: 0;
  }

  /* Never taller than the room: a message that does not fit scrolls inside
     its card rather than run over the wordmark and the disclaimer. */
  [data-fallback="poster"] .error__card {
    display: flex;
    flex-direction: column;
    width: min(340px, 42%);
    max-height: 100%;
  }

  [data-fallback="poster"] .error__core {
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* Balanced, not "pretty": in a card this narrow WebKit's pretty takes a
     line more than Chrome's (six for five at 568 x 320). Balance keeps the
     fewest lines in both and, up to six lines (all a phone needs here),
     breaks them alike, with no word left on its own. */
  [data-fallback="poster"] .error__text {
    text-wrap: balance;
  }
}

/* Where the whole message is taller than its room, it drops its technical
   line and steps its text down. That is 340px tall or less (about 220px of
   room between the masthead and the foot), or under 600px wide and at most
   420px tall, where the card is narrow and so tall: 322px at 480 x 400, in
   303px of room. At 568 x 320 the card is then 189px tall, beside a still
   308px wide. */
@media (max-height: 340px) and (orientation: landscape),
  (max-width: 599px) and (max-height: 420px) and (orientation: landscape) {
  [data-fallback="poster"] .error {
    gap: 14px;
  }

  [data-fallback="poster"] .error__card {
    width: min(340px, 40%);
  }

  [data-fallback="poster"] .error__core {
    padding: 16px 18px 14px;
  }

  [data-fallback="poster"] .error__text {
    font-size: 13px;
  }

  [data-fallback="poster"] .error__detail {
    display: none;
  }
}

.noscript {
  position: absolute;
  top: 50%;
  right: var(--edge);
  left: var(--edge);
  margin: 0;
  color: var(--ink-70);
  font-size: 15px;
  text-align: center;
}
