/*
  Milestone 60 — hand-written CSS for the three vanilla-JS media substitutes
  (see media-interactive.js). Written by hand rather than as Tailwind
  utility classes: app.css is a Tailwind v4 build output checked in as a
  static asset (no build step runs against the live dev server, per this
  port's own convention), so any class not already compiled in from an
  existing template is unavailable. These rules reuse the same design
  tokens (--color-*, --duration-*, --ease-sheet) app.css itself defines
  under :root, so they stay visually identical to the rest of the site.

  Reduced motion: no new @media (prefers-reduced-motion: reduce) rule is
  added here. app.css already has one, global and unscoped
  (`*, *::before, *::after { transition-duration: 0.01ms !important; ... }`,
  see app.css around line 1739) — it already zeroes out every transition
  declared below, including the hero crossfade and the lightbox's own
  fade, without this file needing to repeat it. Section 7 (rail nav
  dropdowns, milestone 91) relies on this same global rule rather than
  adding its own reduced-motion override.
*/

/* 1. Hero carousel crossfade
   Note: .hero-carousel itself does NOT get `position: relative` here — the
   markup already carries Tailwind's `.absolute` utility (home.html gives it
   `absolute inset-0 -z-20`), which is itself a valid positioning context for
   absolutely-positioned children. Adding position here would be redundant
   at best; at worst, since this file is unlayered CSS and Tailwind's own
   utilities live inside `@layer utilities`, an unlayered `position: relative`
   would win the cascade over the layered `.absolute` rule regardless of
   selector specificity or source order, silently breaking the element's own
   `position: absolute` and collapsing it (this was caught live — the hero
   photo didn't render until this rule was removed). */
.hero-carousel__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1200ms var(--ease-sheet);
}
.hero-carousel__slide--active {
  opacity: 1;
}

/* 2. Lightbox gallery */
.lightbox-trigger {
  cursor: pointer;
}

dialog.lightbox-dialog {
  position: fixed;
  inset: 0;
  z-index: 75;
  display: none;
  flex-direction: column;
  width: 100vw;
  height: 100vh;
  max-width: 100vw;
  max-height: 100vh;
  margin: 0;
  padding: 1rem;
  border: 0;
  background: color-mix(in srgb, var(--color-night) 95%, transparent);
  color: var(--color-chalk);
}
dialog.lightbox-dialog[open] {
  display: flex;
}
dialog.lightbox-dialog::backdrop {
  background: transparent;
}
@media (min-width: 640px) {
  dialog.lightbox-dialog {
    padding: 2rem;
  }
}

.lightbox-dialog__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid color-mix(in srgb, var(--color-concrete) 20%, transparent);
  padding-bottom: 0.75rem;
}
.lightbox-dialog__count {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-concrete);
}
.lightbox-dialog__close {
  padding: 0.25rem;
  color: var(--color-chalk);
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 1.5rem;
  line-height: 1;
  transition: color var(--duration-base) var(--ease-sheet);
}
.lightbox-dialog__close:hover {
  color: var(--color-pegasus);
}

.lightbox-dialog__body {
  display: flex;
  min-height: 0;
  flex: 1;
  align-items: center;
  gap: 0.5rem;
  padding-block: 1rem;
}
@media (min-width: 640px) {
  .lightbox-dialog__body {
    gap: 1rem;
  }
}
.lightbox-dialog__nav {
  flex-shrink: 0;
  padding: 0.5rem;
  color: var(--color-concrete);
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 2rem;
  line-height: 1;
  transition: color var(--duration-base) var(--ease-sheet);
}
.lightbox-dialog__nav:hover {
  color: var(--color-pegasus);
}
.lightbox-dialog__image {
  min-height: 0;
  flex: 1;
  max-height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin-inline: auto;
}
.lightbox-dialog__caption {
  border-top: 1px solid color-mix(in srgb, var(--color-concrete) 20%, transparent);
  padding-top: 0.75rem;
  font-size: var(--text-sm);
  color: var(--color-concrete);
}

/* 3. Themed YouTube facade */
.youtube-facade {
  position: relative;
}
.youtube-facade__button {
  position: relative;
  display: flex;
  aspect-ratio: var(--aspect-video);
  width: 100%;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--color-sangar);
  border: 0;
  padding: 0;
  cursor: pointer;
}
.youtube-facade__thumb {
  position: absolute;
  inset: 0;
  height: 100%;
  width: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-sheet);
}
.youtube-facade__button:hover .youtube-facade__thumb,
.youtube-facade__button:focus-visible .youtube-facade__thumb {
  transform: scale(1.03);
}
.youtube-facade__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--color-night) 30%, transparent);
}
.youtube-facade__play {
  position: relative;
  display: flex;
  height: 3.5rem;
  width: 3.5rem;
  align-items: center;
  justify-content: center;
  border: 1px solid color-mix(in srgb, var(--color-chalk) 40%, transparent);
  background: color-mix(in srgb, var(--color-night) 60%, transparent);
  color: var(--color-chalk);
  transition: color var(--duration-base) var(--ease-sheet), border-color var(--duration-base) var(--ease-sheet);
}
.youtube-facade__button:hover .youtube-facade__play,
.youtube-facade__button:focus-visible .youtube-facade__play {
  border-color: var(--color-pegasus);
  color: var(--color-pegasus);
}
.youtube-facade__play svg {
  height: 1.25rem;
  width: 1.25rem;
  transform: translateX(2px);
}

/* 4. Home page sub-unit selector tiles (milestone 78)
   Not vanilla-JS — this is static hover chrome, but for the same reason
   given at the top of this file: each tile's accent colour is set inline
   per-tile via the --tile-accent custom property (home.html), and a
   Tailwind utility referencing a *dynamic* value at that specificity
   (bg-[var(--tile-accent)], text-[var(--tile-accent)]) can't exist as a
   pre-compiled class — app.css is a fixed build output. These rules read
   --tile-accent the same way logo-plate's own inline style already does,
   so the tile still ends up sourcing its colour from UnitBrand, just via
   CSS custom property instead of a compiled utility class. */
.unit-tile:hover .unit-tile__title,
.unit-tile:focus-visible .unit-tile__title,
.unit-tile:hover .unit-tile__cta,
.unit-tile:focus-visible .unit-tile__cta {
  color: var(--tile-accent);
  transition: color var(--duration-fast) var(--ease-sheet);
}
.unit-tile__rule {
  background-color: var(--tile-accent);
  transition: transform var(--duration-slow) var(--ease-sheet);
}
.unit-tile:hover .unit-tile__rule,
.unit-tile:focus-visible .unit-tile__rule {
  transform: scaleX(1);
}

/* 5. Vacancy carousel arrows (milestone 78)
   The original renders two separate arrow sets — absolute side buttons on
   desktop, a centred pair below the grid on mobile — toggled with
   Tailwind's sm:block/sm:hidden. Confirmed live that neither compiles into
   app.css (no existing template used that exact class before this one),
   so sm:block rendered display:none instead of overriding `hidden`. One
   arrow pair, positioned here by hand for both breakpoints, replaces both
   sets; the grid gets left/right padding to keep the arrows off the card
   edges, only when there's anything to scroll (mirrors the original's own
   `scrolls && "sm:px-10"`). */
.vacancy-carousel--scrolls > .grid {
  margin-left: 2.5rem;
  margin-right: 2.5rem;
}
.vacancy-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.vacancy-carousel__arrow--prev {
  left: 0;
}
.vacancy-carousel__arrow--next {
  right: 0;
}
@media (max-width: 639px) {
  .vacancy-carousel--scrolls > .grid {
    margin-left: 0;
    margin-right: 0;
  }
  .vacancy-carousel__arrow {
    position: static;
    transform: none;
    display: inline-flex;
  }
}

/* 6. Line chart (admin/statistics.html, milestone 79)
   Ports src/components/ui/line-chart.tsx. Colour is one of three fixed
   runtime tokens (pegasus/hackle-bright/vacant) picked per chart instance,
   same reasoning as tile 4's own --tile-accent doc comment above: no
   Tailwind utility for a value like this can be pre-compiled into app.css,
   and — confirmed by grep, not assumed — this port has never rendered an
   SVG stroke/fill utility class before this chart, so none of
   stroke-pegasus/fill-pegasus/text-pegasus exist in the compiled output
   either. A `line-chart--*` modifier sets one custom property that every
   descendant (the SVG's line/dots and the sibling tooltip div) reads. */
.line-chart {
  position: relative;
}
.line-chart--pegasus {
  --line-chart-color: var(--color-pegasus);
}
.line-chart--hackle-bright {
  --line-chart-color: var(--color-hackle-bright);
}
.line-chart--vacant {
  --line-chart-color: var(--color-vacant);
}

.line-chart__baseline {
  stroke: color-mix(in srgb, var(--color-concrete) 20%, transparent);
}
.line-chart__reference {
  stroke: color-mix(in srgb, var(--color-concrete) 40%, transparent);
}
.line-chart__reference-label {
  fill: var(--color-concrete);
  font-family: var(--font-data);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.line-chart__crosshair {
  stroke: color-mix(in srgb, var(--color-concrete) 30%, transparent);
}
.line-chart__line {
  stroke: var(--line-chart-color);
}
.line-chart__dot {
  fill: var(--line-chart-color);
  transition: r 100ms ease;
}
.line-chart__dot--active {
  r: 5.5px;
}
.line-chart__axis-label {
  fill: var(--color-concrete);
  font-family: var(--font-data);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.line-chart__tooltip {
  position: absolute;
  top: 0;
  transform: translate(-50%, -100%);
  pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--color-concrete) 25%, transparent);
  background: var(--color-night);
  padding: 0.375rem 0.625rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.line-chart__tooltip-label {
  white-space: nowrap;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-concrete);
}
.line-chart__tooltip-value {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  color: var(--line-chart-color);
}
.line-chart__tooltip-sublabel {
  white-space: nowrap;
  font-size: var(--text-2xs);
  color: var(--color-concrete);
}

/* 7. Rail nav hover/focus dropdowns (fragments/layout.html, milestone 91)
   The original itself never animates this reveal (it's a bare `{expanded
   ? <ul>...</ul> : null}` conditional in nav.tsx) — this is "a reasonable
   equivalent" per that milestone's own brief, using the site's existing
   --duration-base/--ease-sheet tokens rather than inventing new timing.
   grid-template-rows 0fr -> 1fr is used instead of max-height so the panel
   never needs a guessed pixel cap — it works whether a group has three
   sublinks or nine. */
.nav-group__trigger {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.nav-group__chevron {
  height: 0.75rem;
  width: 0.75rem;
  flex-shrink: 0;
  transition: transform var(--duration-fast) var(--ease-sheet);
}
.nav-group--open .nav-group__chevron {
  transform: rotate(180deg);
}
.nav-group__panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--duration-base) var(--ease-sheet), opacity var(--duration-base) var(--ease-sheet);
}
.nav-group--open .nav-group__panel {
  grid-template-rows: 1fr;
  opacity: 1;
}
.nav-group__panel-inner {
  min-height: 0;
  overflow: hidden;
}

/* 8. ORBAT flowchart (orbat.html, milestone 99; connector lines rebuilt
   milestone 101)
   Ports src/components/orbat/flowchart.tsx's box-and-line diagram. The
   original measures every box's real screen position via ref +
   ResizeObserver and draws an SVG overlay — one independent elbow path per
   parent→child edge (`M x1,y1 V midY H x2,y2`, endpoints each box's own
   measured centre), not a merged bar across siblings.

   Milestone 99/100 shipped a pure-CSS equivalent instead (bordered
   ::before/::after pseudo-elements on each <li>, merged into one shared
   horizontal bar per sibling row), reasoning it was "visually the same
   result" for this fixed-box-width layout. That reasoning held only when
   every sibling subtree was the same width. The moment one sibling has a
   far wider expanded subtree than another (confirmed live on /orbat's
   "2 PARA" tab: C Company vs. Mortar Platoon), the shared bar's midpoint —
   50% of the ACCUMULATED width of all sibling columns — drifts away from
   where the boxes actually are, visibly sliding the connector (and the
   parent's own collapse toggle sitting on it) off-centre. That's a real
   divergence from the original's per-edge, ref-measured lines, not a
   rendering detail.

   Milestone 101 removes the border-pseudo-element technique entirely and
   replaces it with the original's own approach: a real SVG overlay
   (`.orbat-flowchart__lines`, added in orbat.html, one per formation
   panel), positions read via getBoundingClientRect() by
   renderAllOrbatLines() (media-interactive.js) after every initial render,
   tab switch, collapse/expand toggle, and window resize — mirroring
   flowchart.tsx's own measure()/ResizeObserver exactly. An asymmetric
   jog — the line to a narrower or off-centre child running left or right
   rather than straight down — is the ORIGINAL's actual behaviour (each
   edge is independent; nothing tries to "average" sibling positions), so
   this port now reproduces it instead of smoothing it into a shared bar. */
.orbat-tab.is-active,
.orbat-tab:hover {
  border-color: var(--orbat-tab-accent, var(--color-pegasus));
  color: var(--orbat-tab-accent, var(--color-pegasus));
}

.orbat-flowchart {
  overflow-x: auto;
  padding-bottom: 1rem;
}
.orbat-flowchart__inner {
  position: relative;
}
.orbat-flowchart__lines {
  z-index: 0;
}
.orbat-flowchart__inner > ul {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: center;
  min-width: fit-content;
  padding: 0 1rem;
}
.orbat-box__children {
  display: flex;
  justify-content: center;
}
/* Scoped to the structural org-chart <li>s only (the root row and each
   box's own children row) — NOT `.orbat-flowchart li` (all descendant
   <li>s), which was a real bug: that blanket selector was also matching
   every billet role/name <li> nested inside `.orbat-box__billets`, three
   or four levels deep inside each box, giving every single billet line
   28px of unwanted top padding plus `display:flex;flex-direction:column;
   align-items:center` — the actual cause of the "way too much empty
   space between names and roles" gap the live site doesn't have. */
.orbat-flowchart__inner > ul > li,
.orbat-box__children > li {
  list-style: none;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 14px each side = 28px total gap between sibling boxes, matching the
     original's real `--orbat-gap` CSS custom property (flowchart.tsx,
     `FLOWCHART_CONFIG.gap = 28`), applied via `gap: var(--orbat-gap)` on
     each FlowNode's own children row. Milestone 100 mistakenly widened
     this to 48px to match the *outer* container's `gap-12` class — but
     that class sits on the top-level row that OrbatTabs always renders
     with exactly one unit (`units={[activeTab.unit]}`), so it never
     actually produces a visible gap; every real sibling gap on the page
     comes from `--orbat-gap`, which is 28px. Reverted to match.
     The 28px top padding is the connector's own vertical length, matching
     flowchart.tsx's `marginTop: var(--orbat-connector-length)` — applied
     unconditionally (including a lone-child row), same as the original,
     which doesn't special-case a single child's vertical gap. */
  padding: 28px 14px 0 14px;
}
.orbat-flowchart__inner > ul > li {
  padding-top: 0;
}
.orbat-box {
  width: 240px;
}
.orbat-box__name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.orbat-box__billets li + li {
  margin-top: 0.125rem;
}
/* Collapsed branch: children (and their own connector lines, drawn on
   those same elements) simply aren't in the render tree while hidden —
   no separate line bookkeeping needed the way the original's SVG overlay
   requires. */
.orbat-box__children[hidden] {
  display: none;
}

/* 9. Scroll-in reveals (milestone 100)
   Ports src/components/motion/motion.tsx's Reveal primitive: hidden =
   {opacity:0, y:10}, shown = {opacity:1, y:0}, 0.34s, ease [.2,.6,.2,1] —
   read directly off that file rather than guessed. Two triggers, matching
   the original's own two Reveal call patterns:
   - [data-reveal-load]: `trigger="load"` — plays once on page load (the
     hero, always already in view), staggered via each element's own
     data-delay (ms), matching the original's per-element delay props.
   - [data-reveal]: `trigger="view"` (the default) — plays once the
     element scrolls into the viewport, via IntersectionObserver with a
     -10% root margin, matching Reveal's own `viewport={{ once: true,
     margin: "-10% 0px" }}`.
   Both fall back to the fully-shown state under prefers-reduced-motion,
   same as useHydrationSafeReducedMotion's whole point. initScrollReveals()
   (media-interactive.js) does the actual class toggling; this is purely
   the two CSS states. */
[data-reveal-load],
[data-reveal] {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.34s cubic-bezier(0.2, 0.6, 0.2, 1),
    transform 0.34s cubic-bezier(0.2, 0.6, 0.2, 1);
}
[data-reveal-load].is-revealed,
[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}
/* Reveal's `variant="scale"` — a barely-perceptible zoom for panels and
   imagery, used on nearly every page's own header/intro Reveal (about,
   contact, documents, faq, join, operations, privacy, training, units).
   Two attribute selectors beats the one attribute selector above on
   specificity, so this wins the `transform` property without needing
   `!important` or source-order games. */
[data-reveal-load][data-reveal-variant="scale"],
[data-reveal][data-reveal-variant="scale"] {
  transform: translateY(8px) scale(0.985);
}
[data-reveal-load][data-reveal-variant="scale"].is-revealed,
[data-reveal][data-reveal-variant="scale"].is-revealed {
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-reveal-load],
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* 10. Staggered reveals (milestone 101)
   Ports Stagger/StaggerItem (motion.tsx) — see media-interactive.js
   §10 (initStaggers) for the per-item transition-delay this depends on.
   A shorter throw (8px) and duration (0.24s) than Reveal's own 10px/0.34s
   — StaggerItem is a distinct constant in the original, not Reveal reused,
   so this doesn't share Reveal's transition value. */
[data-stagger-item] {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.24s cubic-bezier(0.2, 0.6, 0.2, 1),
    transform 0.24s cubic-bezier(0.2, 0.6, 0.2, 1);
}
[data-stagger-item].is-revealed {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-stagger-item] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
