/* ============================================================================
   DTV — Branded loading system
   ----------------------------------------------------------------------------
   One file, loaded by every page shell (admin login, portal, mentor, SME,
   public forms). It answers a single requirement: whenever the product makes
   the user wait, they see the DTV mark — not a generic spinner.

   How it works: the Font Awesome spinner glyph is made transparent and the
   DTV mark is painted as the element's background. Because that element is
   already positioned inside every existing "Loading…" surface, this upgrades
   all of them without touching a single call site — no markup churn, no JS
   injection, and nothing can drift out of sync later.

   The DTV logo is also offered as a reusable block/full-page loader
   (.dtv-load*) for surfaces that need more than an inline glyph.
   ========================================================================= */

/* ── 1. Inline spinner → DTV mark ─────────────────────────────────────────
   Applies to .fa-spinner wherever it appears: buttons, table bodies, panels,
   modals. The glyph is hidden with `color: transparent`, which keeps Font
   Awesome's em-based box (and therefore the surrounding layout) intact, and
   the mark is painted on the element itself.

   The background is set ONLY on the element, never on ::before. FA renders the
   glyph in ::before; setting a background on both would paint two marks — one
   on the element and one inside the glyph box — offset by the icon's
   line-height. So ::before is explicitly reset to `background: none`. */
.fa-spinner,
.fas.fa-spinner,
.fa-solid.fa-spinner {
  color: transparent !important;
  background-image: url('/static/logos/dtv-mark.svg?v=6aec5a22cf');
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  /* The glyph font-width is narrow; give the mark a square box so it reads
     as the logo rather than a squashed sliver. */
  min-width: 1.05em;
  min-height: 1.05em;
  display: inline-block;
  vertical-align: -0.16em;
}

.fa-spinner::before,
.fas.fa-spinner::before,
.fa-solid.fa-spinner::before {
  background: none !important;
  color: transparent !important;
}

/* Stop the 360° glyph rotation (it would spin the logo and read as a fault)
   and breathe instead. !important beats the inline animation from FA. */
.fa-spinner.fa-spin,
.fas.fa-spinner.fa-spin,
.fa-solid.fa-spinner.fa-spin {
  animation: dtvMarkBreathe 1.35s ease-in-out infinite !important;
}

@keyframes dtvMarkBreathe {
  0%, 100% { opacity: .45; transform: scale(.94); }
  50%      { opacity: 1;   transform: scale(1); }
}

/* ── 1b. Border-spinner class → DTV mark ─────────────────────────────────
   `.spinner` is the CSS-drawn ring used by the portal/mentor/SME shells. It is
   replaced outright: the ring border is removed and the mark takes its place
   at the same footprint. This file is linked after each page's inline <style>,
   so an equal-specificity rule here wins over the ring definition. */
.spinner {
  border: 0 !important;
  border-radius: 0 !important;
  background: url('/static/logos/dtv-mark.svg?v=6aec5a22cf') center / contain no-repeat !important;
  animation: dtvMarkBreathe 1.35s ease-in-out infinite !important;
}

/* ── 2. Block loader — sits inside a panel/list while its data loads ────── */
.dtv-load {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 34px 18px;
  color: #5B6B77;
  font-size: .8125rem;
}
.dtv-load-mark {
  width: 26px;
  height: 26px;
  object-fit: contain;
  flex-shrink: 0;
  animation: dtvMarkBreathe 1.35s ease-in-out infinite;
}
.dtv-load-msg { line-height: 1.5; }

/* ── 3. Full-page loader — first paint of any portal/login surface ─────── */
.dtv-load-page {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: #F5F8F9;
}
/* 4x the previous 168px. The vh term keeps the mark from pushing the message
   below the fold on a short viewport; the max() floor stops that expression
   going negative, which would invalidate the whole min() and drop the size. */
.dtv-load-page-logo {
  width: min(672px, 92vw, max(168px, calc((100vh - 220px) * 2.318)));
  height: auto;
  animation: dtvMarkBreathe 1.9s ease-in-out infinite;
}
.dtv-load-bar {
  width: min(672px, 92vw);
  max-width: 60vw;
  height: 3px;
  border-radius: 99px;
  background: #E4EAED;
  overflow: hidden;
}
.dtv-load-bar > i {
  display: block;
  height: 100%;
  width: 38%;
  border-radius: 99px;
  background: linear-gradient(90deg, #31B39A, #F5822B, #1E4E8C);
  animation: dtvLoadSlide 1.5s cubic-bezier(.55, .1, .45, .9) infinite;
}
.dtv-load-page-msg {
  font-size: .8125rem;
  font-weight: 600;
  color: #0F1B26;
  letter-spacing: -.005em;
}
@keyframes dtvLoadSlide {
  0%   { transform: translateX(-105%); }
  100% { transform: translateX(280%); }
}

/* ── 3b. Stage loader — THE waiting state for every page and sub-page ────
   One component for all of them: app.js renderPage(), the shell's initial
   #page-content markup, dash/host.js, modules/10-dtv-tasks.js and the portal
   dashboards. Never a 28px ring or a 32px FA glyph again.

   Centring: the block fills the space under the header (100vh minus header
   and the 24px main padding) and centres on both axes, so the mark sits in
   the middle of the page rather than at the top of a 60vh box.
   Size: 440px, down from 672px — still comfortably readable, no longer
   dominating the viewport. Semi-transparent, as asked: the logo sits at
   ~55% and breathes between 45% and 72%, so it reads as a watermark while
   the bar carries the progress. */
.dtv-stage-load {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  /* Fill the CONTENT AREA — the column to the right of the sidebar, from just
     under the top bar to the bottom of the viewport — then centre on both axes.

     --dtv-load-h is the MEASURED height of the content area: the viewport
     height minus the top bar, minus #page-content's own top and bottom padding.
     app.js writes it in dtvLoaderFit() every time a loader is painted and on
     resize, because the figure differs per viewport (710px on an 844px phone,
     670px on an 820px laptop viewport). Hard-coding it was the actual bug: no
     single constant is right for both, and the wrong one left the mark sitting
     high with ~170px of dead space under it. The calc() fallback covers only
     the very first paint, before any measurement has run. */
  min-height: var(--dtv-load-h, calc(100dvh - 160px));
  /* Asymmetric padding is deliberate, and it fixes TWO separate offsets:
       1. Flexbox centres the whole group (logo + bar + label), but the bar and
          label hang BELOW the logo, so the mark itself rides high by half their
          height: bar 3px + two 18px gaps + label 20px = 59px, i.e. 29.5px.
       2. The logo PNG is not optically centred in its own canvas — its artwork
          sits 18.5px high in an 818px-tall image, which is 2.26% of the height.
          At 440px wide (190px tall) that is another 4.3px high.
     So the top needs 59 + (2 x 4.3) = 67.6px more padding than the bottom;
     100 - 32 = 68. That puts the VISIBLE mark, not merely the <img> box, on the
     centre line of the content area. */
  padding: 100px 18px 32px;
  text-align: center;
  box-sizing: border-box;
}
.dtv-stage-load-logo {
  width: min(440px, 78%, max(176px, calc((100vh - 300px) * 2.318)));
  height: auto;
  display: block;
  margin: 0 auto;
  opacity: .55;
  animation: dtvStageBreathe 2.1s ease-in-out infinite;
}

/* Inset variant — for a panel that already draws its own header and tab row
   above the waiting area (Virtual Incubator's #vi-body, the portfolio
   tracker). Filling 100vh there would push the mark far below the fold and
   double-count the chrome, so this variant centres inside the space it
   actually has while keeping the identical logo, bar and message. */
.dtv-stage-load.is-inset {
  /* The host panel already draws a header and a tab row above the waiting
     area, so --dtv-load-h (which assumes the loader starts right under the top
     bar) is the wrong frame here. dtvLoaderFit() measures THIS element's own
     top instead and writes the remaining height inline, so the mark centres in
     the space actually visible below the tabs. min-height:0 is the pre-measure
     state: no 100dvh jump before JS runs. */
  min-height: 0;
  width: 100%;
  /* Same two corrections as the full-page rule, scaled to the 380px logo:
     59px for the bar/gaps/label, plus 2 x 3.7px for the PNG's own imbalance.
     92 - 26 = 66. */
  padding: 92px 18px 26px;
}
.dtv-stage-load.is-inset .dtv-stage-load-logo {
  width: min(380px, 72%, max(160px, calc((100vh - 460px) * 2.318)));
}
.dtv-stage-load.is-inset .dtv-load-bar {
  width: min(380px, 72%);
}
@keyframes dtvStageBreathe {
  0%, 100% { opacity: .45; transform: scale(.98); }
  50%      { opacity: .72; transform: scale(1); }
}
.dtv-stage-load .dtv-load-bar {
  width: min(440px, 78%);
  max-width: none;
}
.dtv-stage-load-msg {
  font-size: .8125rem;
  font-weight: 600;
  color: #5B6B77;
  letter-spacing: -.005em;
}

/* ── 4. Branded button busy state ────────────────────────────────────────
   Buttons that swap their label for a spinner keep the DTV mark from rule 1;
   this only guards the disabled look so it reads as deliberate. */
button:disabled > .fa-spinner,
.btn:disabled > .fa-spinner { opacity: 1; }

/* ── 5. Accessibility ────────────────────────────────────────────────────
   Motion is decorative, never load-bearing: with reduced motion the mark
   stays fully visible and simply stops moving. */
@media (prefers-reduced-motion: reduce) {
  .fa-spinner.fa-spin,
  .dtv-load-mark,
  .dtv-load-page-logo,
  .dtv-stage-load-logo,
  .dtv-load-bar > i {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .dtv-load-bar > i { width: 100%; opacity: .5; }
}
