/* =========================================================
   Announcement bar
   The promo band above the navbar. Markup contract lives in
   includes/announcement-bar.php, behaviour in
   src/js/announcement-bar.js.

   Two layers of state:
     html.anno-on   — the bar is occupying space, navbar rides down
     .anno.is-in    — the bar itself is revealed
   They are toggled together so the band and the navbar move as one.
   ========================================================= */

:root {
  --anno-h: 2.9rem;

  /* Titan (default): poster crimson on topographic black. The display red is
     lifted off the poster's #a81820 because at bar type sizes the poster value
     falls under 3:1 on near-black.

     Alpha variants are spelled out rather than derived — same reasoning as
     theme-dark.css, and it keeps a theme swap to one flat block. */
  --anno-accent: #a81820;
  --anno-accent-lit: #e0353f;
  --anno-accent-hot: #ff5a63;
  --anno-accent-pale: #ffd7d9;

  --anno-accent-18: rgba(168, 24, 32, 0.18);
  --anno-accent-55: rgba(168, 24, 32, 0.55);
  --anno-lit-00: rgba(224, 53, 63, 0);
  --anno-lit-28: rgba(224, 53, 63, 0.28);
  --anno-lit-34: rgba(224, 53, 63, 0.34);
  --anno-lit-45: rgba(224, 53, 63, 0.45);
  --anno-lit-50: rgba(224, 53, 63, 0.5);
  --anno-lit-55: rgba(224, 53, 63, 0.55);

  --anno-bg: #08080a;
  --anno-bg-00: rgba(8, 8, 10, 0);
  --anno-bg-86: rgba(8, 8, 10, 0.86);
  --anno-bg-96: rgba(8, 8, 10, 0.96);

  --anno-ink: #ffffff;
  --anno-ink-soft: rgba(255, 255, 255, 0.72);
  --anno-texture: url(/assets/images/seamless-topo.jpg);
  --anno-texture-size: 420px;
  --anno-texture-opacity: 0.5;

  --anno-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --anno-ease-spring: cubic-bezier(0.16, 1.08, 0.3, 1);
  --anno-shift: 460ms;
}

/* Timber Van Kits: tan on the wood-grain plate from their poster. */
.anno--timber {
  --anno-accent: #8f6c3f;
  --anno-accent-lit: #d3a970;
  --anno-accent-hot: #f0cb96;
  --anno-accent-pale: #fff0d8;

  --anno-accent-18: rgba(143, 108, 63, 0.18);
  --anno-accent-55: rgba(143, 108, 63, 0.55);
  --anno-lit-00: rgba(211, 169, 112, 0);
  --anno-lit-28: rgba(211, 169, 112, 0.28);
  --anno-lit-34: rgba(211, 169, 112, 0.34);
  --anno-lit-45: rgba(211, 169, 112, 0.45);
  --anno-lit-50: rgba(211, 169, 112, 0.5);
  --anno-lit-55: rgba(211, 169, 112, 0.55);

  --anno-bg: #0b0906;
  --anno-bg-00: rgba(11, 9, 6, 0);
  --anno-bg-86: rgba(11, 9, 6, 0.86);
  --anno-bg-96: rgba(11, 9, 6, 0.96);

  --anno-texture: url(/assets/brands/campervan-timberline1x.jpg);
  --anno-texture-size: 520px;
  --anno-texture-opacity: 0.24;
}

/* =========================================================
   Shell
   ========================================================= */

.anno {
  position: fixed;
  top: 0;
  left: 0;
  /* One above .navbar (1000000000) so the band stays lit while a mega menu
     dims the page behind it. */
  z-index: 1000000001;

  width: 100%;
  height: var(--anno-h);
  overflow: hidden;

  background: var(--anno-bg);
  isolation: isolate;

  transition:
    transform var(--anno-shift) var(--anno-ease-spring),
    opacity 260ms ease,
    visibility 260ms ease;
}

/* Without JS the band is simply there. .anno--js is added inline the moment
   the element closes, which is what arms the entrance. */
.anno--js {
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
}

.anno--js.is-in {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
}

.anno--js.is-out,
.anno--js.is-closing {
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
}

/* The dismissal collapse gets a shorter, snappier curve than the entrance —
   an exit that springs reads as a mistake. */
.anno--js.is-closing {
  transition:
    transform 320ms cubic-bezier(0.5, 0, 0.75, 0),
    opacity 200ms ease,
    visibility 200ms ease;
}

/* Navbar offset. `translate` rather than `transform`: .navbar's transform is
   already spoken for by the service-page filter swap
   (html.filter-nav-swap-active), and the two must not fight. */
html.anno-on .navbar {
  translate: 0 var(--anno-h);
}

/* Bottom rim — the one hard edge that separates the band from the nav glass. */
.anno::after {
  content: "";
  position: absolute;
  z-index: 6;
  right: 0;
  bottom: 0;
  left: 0;
  height: 2px;

  background: linear-gradient(
    90deg,
    transparent,
    var(--anno-accent) 16%,
    var(--anno-accent-hot) 50%,
    var(--anno-accent) 84%,
    transparent
  );
  opacity: 0.85;
}

/* =========================================================
   Animated backdrop
   Four stacked layers: drifting topography, breathing glow,
   crawling trail, sweeping sheen.
   ========================================================= */

.anno__topo {
  position: absolute;
  z-index: 0;
  inset: 0;
  overflow: hidden;
  opacity: var(--anno-texture-opacity);
}

/* Oversized by exactly one tile in each direction so the pan never exposes an
   edge, however narrow the viewport. */
.anno__topo::before {
  content: "";
  position: absolute;
  top: -25%;
  left: 0;
  width: calc(100% + (var(--anno-texture-size) * 2));
  height: 150%;

  background-image: var(--anno-texture);
  background-repeat: repeat;
  background-size: var(--anno-texture-size) var(--anno-texture-size);

  animation: anno-pan 64s linear infinite;
}

@keyframes anno-pan {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(calc(var(--anno-texture-size) * -1), 0, 0);
  }
}

.anno__glow {
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;

  background: radial-gradient(
    56% 210% at 50% 118%,
    rgba(255, 255, 255, 0.05),
    transparent 70%
  );
  animation: anno-breathe 6.5s ease-in-out infinite;
}

.anno__glow::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    56% 210% at 50% 118%,
    var(--anno-accent),
    transparent 68%
  );
  opacity: 0.55;
}

@keyframes anno-breathe {
  0%,
  100% {
    opacity: 0.7;
    transform: scaleX(0.94);
  }
  50% {
    opacity: 1;
    transform: scaleX(1.06);
  }
}

/* The dashed hiking route the posters run under their type. */
.anno__trail {
  position: absolute;
  z-index: 2;
  inset: 0;
  pointer-events: none;
  opacity: 0.45;
}

.anno__trail svg {
  display: block;
  width: 100%;
  height: 100%;
}

.anno__trail path {
  fill: none;
  stroke: rgba(255, 255, 255, 0.55);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-dasharray: 7 10;
  animation: anno-trail 3.4s linear infinite;
}

@keyframes anno-trail {
  to {
    /* Two full dash periods, so the loop point is invisible. */
    stroke-dashoffset: -34;
  }
}

.anno__sheen {
  position: absolute;
  z-index: 3;
  top: -60%;
  bottom: -60%;
  left: -30vw;
  width: 16vw;
  pointer-events: none;

  background: linear-gradient(
    100deg,
    transparent,
    rgba(255, 255, 255, 0.1) 40%,
    rgba(255, 255, 255, 0.3) 50%,
    rgba(255, 255, 255, 0.1) 60%,
    transparent
  );
  mix-blend-mode: screen;
  transform: skewX(-18deg) translate3d(0, 0, 0);
  animation: anno-sheen 9s cubic-bezier(0.45, 0, 0.25, 1) infinite;
}

@keyframes anno-sheen {
  0%,
  6% {
    opacity: 0;
    transform: skewX(-18deg) translate3d(0, 0, 0);
  }
  12% {
    opacity: 1;
  }
  44% {
    opacity: 0;
    transform: skewX(-18deg) translate3d(140vw, 0, 0);
  }
  100% {
    opacity: 0;
    transform: skewX(-18deg) translate3d(140vw, 0, 0);
  }
}

/* =========================================================
   Layout
   Side columns flex, the message column sizes to its content,
   so the copy sits optically centred in the bar.
   ========================================================= */

.anno__inner {
  position: relative;
  z-index: 5;

  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  height: 100%;
  /* Right gutter clears the close button, which is positioned to the edge. */
  padding: 0 2.9rem 0 1rem;
}

.anno__track {
  display: grid;
  grid-column: 2;
  justify-self: center;
  min-width: 0;
  max-width: 100%;
  perspective: 700px;
}

/* Every slide shares one grid cell, so the track sizes to the widest of them
   and a rotation cross-fades in place instead of jumping the layout. */
.anno__slide {
  grid-area: 1 / 1;

  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.72rem;
  min-width: 0;
}

.anno--js .anno__slide:not(.is-active):not(.is-leaving) {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* =========================================================
   Message parts
   ========================================================= */

.anno__brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.anno__mark {
  display: block;
  flex: 0 0 auto;
  width: 1.3rem;
  color: var(--anno-accent-lit);
  filter: drop-shadow(0 0 7px var(--anno-lit-45));
  animation: anno-mark-pulse 6.5s ease-in-out infinite;
}

.anno__mark svg,
.anno__mark img {
  display: block;
  width: 100%;
  height: auto;
}

.anno--timber .anno__mark {
  width: 1.05rem;
}

@keyframes anno-mark-pulse {
  0%,
  100% {
    opacity: 0.9;
    transform: scale(1);
  }
  50% {
    opacity: 1;
    transform: scale(1.09);
  }
}

.anno__eyebrow {
  padding: 0.2rem 0.45rem;

  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.66);

  background: var(--anno-accent-18);
  border: 1px solid var(--anno-lit-34);
  border-radius: 2px;
}

.anno__lead {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  min-width: 0;
}

/* The poster's headline number, carried across as a shimmering gradient fill. */
.anno__headline {
  font-size: 1.16rem;
  font-weight: 900;
  letter-spacing: -0.015em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;

  background-image: linear-gradient(
    100deg,
    var(--anno-accent-lit) 0%,
    var(--anno-accent-hot) 40%,
    var(--anno-accent-pale) 50%,
    var(--anno-accent-hot) 60%,
    var(--anno-accent-lit) 100%
  );
  background-size: 260% 100%;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;

  animation: anno-shimmer 6.5s ease-in-out infinite;
}

@keyframes anno-shimmer {
  0%,
  100% {
    background-position: 130% 50%;
  }
  50% {
    background-position: -30% 50%;
  }
}

.anno__amount {
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--anno-accent-lit);
}

.anno__subject {
  font-size: 0.84rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--anno-ink);
}

.anno__detail {
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--anno-ink-soft);
}

.anno__kicker {
  position: relative;
  padding-left: 0.62rem;

  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.92);
}

.anno__kicker::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 0.28rem;
  height: 0.28rem;
  margin-top: -0.14rem;
  background: var(--anno-accent-lit);
  transform: rotate(45deg);
}

.anno__rule {
  flex: 0 0 auto;
  width: 1px;
  height: 1.05rem;
  background: linear-gradient(
    180deg,
    transparent,
    var(--anno-lit-55),
    transparent
  );
}

/* =========================================================
   CTA
   ========================================================= */

/* The anchor carries .tv-cta.tv-cta--red, so the whole skin — gradient,
   border, top sheen, shadows, and the hover / active / focus states — comes
   from src/styles/cta.css and can never drift from the site's other red
   buttons. Everything here is size.

   cta.css drives every dimension off custom properties, and redefining those
   on the element itself beats the :root values by cascade, not specificity —
   so none of this has to out-weigh a stylesheet that loads after this one. */
.anno__cta {
  --tv-cta-height: 1.75rem;
  --tv-cta-pad-y: 0.3rem;
  --tv-cta-pad-x: 0.72rem;
  --tv-cta-gap: 0.4rem;
  --tv-cta-font-size: 0.72rem;
}

/* cta.css clips the button so its sheen respects the radius, but the pulse
   ring below spreads *outside* the border box and would be cut off. Let the
   ring out and re-clip the sheen on itself instead.

   Needs (0,2,0): this is the one declaration here that is not a custom
   property, so it is the one that has to out-weigh cta.css — which loads
   after this file and would otherwise win the tie. */
.anno .anno__cta {
  overflow: visible;
}

.anno__cta::before {
  border-radius: inherit;
}

/* The halo lives on a pseudo-element because the entrance stagger claims the
   CTA's own `animation` property — one element cannot run both. */
.anno__cta::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  animation: anno-cta-pulse 3.8s ease-out 1.4s infinite;
}

.anno__cta i {
  font-size: 0.62rem;
  transition: transform 220ms var(--anno-ease);
}

.anno__cta:hover i,
.anno__cta:focus-visible i {
  transform: translateX(3px);
}

@keyframes anno-cta-pulse {
  0% {
    box-shadow: 0 0 0 0 var(--anno-lit-50);
  }
  70%,
  100% {
    box-shadow: 0 0 0 9px var(--anno-lit-00);
  }
}

/* =========================================================
   Entrance + rotation choreography
   One stagger keyframe drives both: the reveal on load and every
   subsequent slide change.
   ========================================================= */

.anno--js .anno__slide.is-active > * {
  animation: anno-part-in 620ms var(--anno-ease-spring) both;
  animation-delay: calc(var(--i, 0) * 52ms + 90ms);
}

.anno--js .anno__slide.is-leaving > * {
  animation: anno-part-out 380ms var(--anno-ease) both;
  animation-delay: calc(var(--i, 0) * 26ms);
}

@keyframes anno-part-in {
  from {
    opacity: 0;
    transform: translate3d(0, 82%, 0) rotateX(-42deg) scale(0.94);
    filter: blur(4px);
  }
  62% {
    filter: blur(0);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}

@keyframes anno-part-out {
  from {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
  to {
    opacity: 0;
    transform: translate3d(0, -75%, 0) rotateX(46deg) scale(0.95);
    filter: blur(5px);
  }
}

/* =========================================================
   Dot pager (only rendered when more than one is live)
   ========================================================= */

.anno__dots {
  display: flex;
  grid-column: 3;
  align-items: center;
  justify-self: end;
  gap: 0.34rem;
}

/* The model stylesheets style bare <button> as a red block — every property
   they set has to be answered here. See docs/gotchas.md. */
.anno__dot {
  position: relative;
  display: block;
  width: 0.44rem;
  height: 0.44rem;
  margin: 0;
  padding: 0;
  overflow: hidden;

  font: inherit;
  text-transform: none;
  letter-spacing: normal;
  color: inherit;

  background: rgba(255, 255, 255, 0.24);
  border: 0;
  border-radius: 99px;
  cursor: pointer;

  transition:
    width 380ms var(--anno-ease-spring),
    background-color 240ms ease;
}

.anno__dot.is-active {
  width: 1.25rem;
  background: var(--anno-lit-28);
}

.anno__dot-fill {
  position: absolute;
  inset: 0;
  transform-origin: 0 50%;
  transform: scaleX(0);
  background: var(--anno-accent-lit);
}

/* Fills across the dwell time, so the pager doubles as a progress readout.
   Duration is written by the JS from data-anno-interval. */
.anno__dot.is-active .anno__dot-fill {
  animation: anno-dot-fill var(--anno-dwell, 7200ms) linear both;
}

.anno--paused .anno__dot.is-active .anno__dot-fill {
  animation-play-state: paused;
}

@keyframes anno-dot-fill {
  to {
    transform: scaleX(1);
  }
}

/* =========================================================
   Close button
   Hidden until the band is hovered — the X strokes draw
   themselves in from the centre, then spin under the pointer.
   ========================================================= */

.anno__close {
  position: absolute;
  z-index: 7;
  top: 0;
  right: 0;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 100%;
  margin: 0;
  padding: 0;

  font: inherit;
  text-transform: none;
  letter-spacing: normal;
  color: rgba(255, 255, 255, 0.88);

  background: linear-gradient(
    90deg,
    var(--anno-bg-00),
    var(--anno-bg-86) 48%,
    var(--anno-bg-96)
  );
  border: 0;
  border-radius: 0;
  cursor: pointer;

  /* Gated with pointer-events, not visibility: a visibility:hidden button
     leaves the tab order entirely, so :focus-within could never reveal it and
     the bar would be undismissable by keyboard. Tabbing to it still works
     here — pointer-events does not affect focusability — and focus then trips
     the same reveal a pointer does. */
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 200ms var(--anno-ease),
    color 200ms ease;
}

.anno:hover .anno__close,
.anno:focus-within .anno__close {
  opacity: 1;
  pointer-events: auto;
}

.anno__close:hover {
  color: var(--anno-accent-hot);
}

.anno__close-x {
  position: relative;
  display: block;
  width: 0.8rem;
  height: 0.8rem;
}

.anno__close-x::before,
.anno__close-x::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.6px;
  border-radius: 2px;
  background: currentColor;
  transform-origin: 50% 50%;
  transition: transform 320ms var(--anno-ease-spring);
}

/* Resting: the strokes are collapsed to a nub in the middle. */
.anno__close-x::before {
  transform: translateY(-50%) rotate(45deg) scaleX(0.001);
}

.anno__close-x::after {
  transform: translateY(-50%) rotate(-45deg) scaleX(0.001);
}

.anno:hover .anno__close-x::before,
.anno__close:focus-visible .anno__close-x::before {
  transform: translateY(-50%) rotate(45deg) scaleX(1);
}

.anno:hover .anno__close-x::after,
.anno__close:focus-visible .anno__close-x::after {
  transform: translateY(-50%) rotate(-45deg) scaleX(1);
}

/* …and a quarter turn once the pointer is on the button itself. */
.anno__close:hover .anno__close-x::before {
  transform: translateY(-50%) rotate(135deg) scaleX(1);
}

.anno__close:hover .anno__close-x::after {
  transform: translateY(-50%) rotate(45deg) scaleX(1);
}

.anno__close:focus-visible {
  outline: 2px solid var(--anno-accent-hot);
  outline-offset: -3px;
}

/* Nothing to hover on touch, so the affordance is always drawn. */
@media (hover: none) {
  .anno__close {
    opacity: 1;
    pointer-events: auto;
  }

  .anno__close-x::before {
    transform: translateY(-50%) rotate(45deg) scaleX(1);
  }

  .anno__close-x::after {
    transform: translateY(-50%) rotate(-45deg) scaleX(1);
  }
}

/* =========================================================
   Responsive
   Copy sheds from the outside in as the bar narrows: fine print
   first, then the qualifier, then the subject.
   ========================================================= */

@media only screen and (max-width: 1400px) {
  .anno__kicker {
    display: none;
  }
}

@media only screen and (max-width: 1180px) {
  .anno__eyebrow,
  .anno__detail,
  .anno__rule--detail {
    display: none;
  }
}

@media only screen and (max-width: 900px) {
  :root {
    --anno-h: 2.7rem;
  }

  .anno__inner {
    padding-right: 2.6rem;
    padding-left: 0.75rem;
  }

  .anno__slide {
    gap: 0.55rem;
  }

  .anno__headline {
    font-size: 1.02rem;
  }

  .anno__amount,
  .anno__subject {
    font-size: 0.74rem;
  }

  .anno__cta {
    --tv-cta-height: 1.6rem;
    --tv-cta-pad-y: 0.26rem;
    --tv-cta-pad-x: 0.6rem;
    --tv-cta-font-size: 0.67rem;
  }
}

@media only screen and (max-width: 640px) {
  :root {
    --anno-h: 2.55rem;
  }

  .anno__inner {
    grid-template-columns: minmax(0, 1fr);
    padding-right: 2.4rem;
    padding-left: 0.6rem;
  }

  .anno__track,
  .anno__dots {
    grid-column: 1;
  }

  /* The pager would crowd a 320px bar; the rotation still runs. */
  .anno__dots {
    display: none;
  }

  .anno__subject,
  .anno__rule {
    display: none;
  }

  .anno__mark {
    width: 1.1rem;
  }

  .anno__headline {
    font-size: 0.95rem;
  }

  .anno__amount {
    font-size: 0.7rem;
  }

  .anno__trail {
    opacity: 0.3;
  }
}

@media only screen and (max-width: 400px) {
  /* Clipped, not display:none — the label is this link's only accessible
     name, and hiding it outright would leave a bare arrow with none. */
  .anno__cta span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* Label clipped away — the pill shrinks to a round arrow chip. */
  .anno__cta {
    --tv-cta-pad-x: 0.52rem;
    --tv-cta-gap: 0;
  }

  .anno__cta i {
    font-size: 0.7rem;
  }
}

/* =========================================================
   Reduced motion
   Every loop stops; the bar still appears, dismisses and rotates,
   just without the choreography.
   ========================================================= */

@media (prefers-reduced-motion: reduce) {
  .anno,
  .anno__topo::before,
  .anno__glow,
  .anno__trail path,
  .anno__sheen,
  .anno__mark,
  .anno__headline,
  .anno__cta::after,
  .anno__dot-fill,
  .anno--js .anno__slide.is-active > *,
  .anno--js .anno__slide.is-leaving > * {
    animation: none !important;
  }

  .anno__sheen {
    display: none;
  }

  .anno--js .anno__slide.is-leaving {
    display: none;
  }

  .anno--js .anno__slide.is-active > * {
    opacity: 1;
    transform: none;
    filter: none;
  }

  .anno,
  html.anno-on .navbar,
  .anno__close,
  .anno__close-x::before,
  .anno__close-x::after,
  .anno__dot {
    transition-duration: 1ms;
  }
}
