/*!
 * Template Name: Hostina Template
 * Author: hostk.com
 * Version: 1.0
 * Description: Modern Responsive Bootstrap Hosting Template
 * Created: 2025
 */
 
/* /////////////////// HERO HOME STYLES /////////////// */
/* /////////////////////////////////////////////////////// */

/* Domain search field with a fixed Unicorn orb on the left */
.domain-search-fx {
  --domain-orb-padding: 5px;
  --domain-glow-angle: 0deg;
  position: relative;
  isolation: isolate;
  min-height: 80px;
  overflow: hidden;
  background: rgba(10, 10, 12, 0.35);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid rgba(56, 130, 255, 0.85);
  border-radius: 999px;
  box-shadow:
    inset 0 1px 0 rgba(140, 190, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35),
    0 0 18px rgba(50, 120, 255, 0.28),
    0 8px 24px rgba(0, 0, 0, 0.35);
  transition: box-shadow 280ms ease, border-color 280ms ease;
}
/* Animated glow ring on focus: spinning conic gradient masked to a 2px border. */
@property --domain-glow-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
.domain-search-fx::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--domain-glow-angle),
    rgba(56, 130, 255, 0.85) 0deg,
    rgba(56, 130, 255, 0) 210deg,
    rgba(160, 210, 255, 1) 295deg,
    rgba(56, 130, 255, 0.85) 360deg
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity 280ms ease;
  z-index: 4;
}
.domain-search-fx:focus-within {
  border-color: transparent;
  box-shadow:
    inset 0 1px 0 rgba(170, 215, 255, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35),
    0 0 32px rgba(80, 150, 255, 0.5),
    0 8px 28px rgba(0, 0, 0, 0.4);
}
.domain-search-fx:focus-within::before {
  opacity: 1;
  animation: domainGlowSpin 3.6s linear infinite;
}
@keyframes domainGlowSpin {
  to { --domain-glow-angle: 360deg; }
}
/* Fallback for browsers without @property: animate via background-position rotation hack */
@supports not (background: conic-gradient(from var(--domain-glow-angle), red, blue)) {
  .domain-search-fx::before { display: none; }
}
.domain-search-fx__input {
  position: relative;
  z-index: 2;
  min-height: 80px;
  border: 1px solid transparent !important;
  border-radius: 999px !important;
  background: transparent !important;
  background-color: transparent !important;
  box-shadow: none !important;
  padding-left: 2.75rem !important;
  padding-right: calc(80px + 1rem) !important;
}
.domain-search-fx__input:hover,
.domain-search-fx__input:focus,
.domain-search-fx__input:active {
  background: transparent !important;
  background-color: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
}
.domain-search-fx .icon {
  z-index: 3;
}
/* Circular submit button perfectly fitted to the right end of the field. */
#hero-home .search-form .domain-search-fx__submit,
.domain-search-fx__submit {
  position: absolute;
  top: 50%;
  right: var(--domain-orb-padding);
  transform: translateY(-50%);
  width: calc(80px - (var(--domain-orb-padding) * 2));
  height: calc(80px - (var(--domain-orb-padding) * 2));
  min-height: 0;
  border-radius: 999px;
  border: 0;
  padding: 0;
  line-height: 1;
  font-size: 1.25rem;
  font-weight: 600;
  background: #ffffff !important;
  background-color: #ffffff !important;
  background-image: none !important;
  color: #0a0a0c;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 3;
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  transition: background-color 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}
#hero-home .search-form .domain-search-fx__submit::before {
  content: none;
}
#hero-home .search-form .domain-search-fx__submit:hover,
.domain-search-fx__submit:hover,
.domain-search-fx__submit:focus-visible {
  background: #eaf1ff;
  box-shadow: 0 6px 22px rgba(80, 150, 255, 0.45);
  outline: none;
  transform: translateY(-50%);
}
.domain-search-fx__submit:focus-visible {
  outline: 2px solid rgba(170, 215, 255, 0.95);
  outline-offset: 3px;
}
#hero-home .search-form .domain-search-fx__submit:active,
.domain-search-fx__submit:active {
  transform: translateY(-50%) scale(0.96);
  background: #ffffff;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}
#hero-home {
  min-height: 850px;
  background-color: var(--color-primary);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

.hero-content {
  position: relative;
  z-index: 3;
  width: auto;
}

/* Unicorn Studio animated hero background — native 1440x900 scaled to cover. */
.hero-unicorn-bg {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
}
.hero-unicorn-bg > div[data-us-project],
.hero-unicorn-bg > div[data-us-project-src] {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1440px;
  height: 900px;
  transform: translate(-50%, -50%) scale(var(--hero-us-scale, 1));
  transform-origin: center center;
}

#hero-home::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(
      4px 4px at 10% 20%,
      rgba(255, 255, 255, 0.9),
      transparent
    ),
    radial-gradient(
      2.5px 2.5px at 25% 65%,
      rgba(255, 255, 255, 0.7),
      transparent
    ),
    radial-gradient(3px 3px at 80% 45%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(2px 2px at 50% 15%, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(
      2.5px 2.5px at 90% 75%,
      rgba(255, 255, 255, 0.5),
      transparent
    ),
    radial-gradient(3px 3px at 70% 35%, rgba(255, 255, 255, 0.7), transparent),
    radial-gradient(2px 2px at 30% 85%, rgba(255, 255, 255, 0.6), transparent),
    radial-gradient(
      2.5px 2.5px at 15% 50%,
      rgba(255, 255, 255, 0.5),
      transparent
    ),
    radial-gradient(3px 3px at 60% 70%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(2px 2px at 85% 25%, rgba(255, 255, 255, 0.7), transparent);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
}

.hero-ellipse {
  position: absolute;
  top: 22%;
  left: 50%;
  transform: translateX(-50%);
  width: 16rem;
  height: 16rem;
  box-shadow: 0 0 28px #ffffff30;
  border-radius: 50%;
  animation: ellipsePulse 6s infinite alternate;
}

@keyframes ellipsePulse {
  0% {
    transform: translateX(-50%) scale(1);
  }
  100% {
    transform: translate(-50%) scale(1.5);
  }
}
#hero-home input {
  font-size: 2rem;
  height: 100%;
  padding-right: 5.4rem;
  border: 1px solid transparent;
  border-radius: 10px;
  background: #1a1a1a!important;
  color: #f0f0f0!important;
  outline: none;
  transition: all 0.3s ease;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6),
              0 0 6px rgba(255, 255, 255, 0.05);
}

#hero-home input:hover {
  border-color: transparent;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.1);
}

#hero-home input:focus {
  border: 1px solid transparent;
  background: linear-gradient(#1a1a1a, #1a1a1a) padding-box,
              linear-gradient(90deg, #888, #555) border-box;
  box-shadow: 0 0 10px rgba(200, 200, 200, 0.3);
}

#hero-home input:active {
  border: 1px solid transparent;

}

#hero-home input::placeholder {
  color: var(--color-text);
}

#hero-home .domain-search-fx .domain-search-fx__input,
#hero-home .domain-search-fx .domain-search-fx__input:hover,
#hero-home .domain-search-fx .domain-search-fx__input:focus,
#hero-home .domain-search-fx .domain-search-fx__input:active {
  background: transparent !important;
  background-color: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
  -webkit-box-shadow: none !important;
  appearance: none;
  border-radius: 999px !important;
}

#hero-home .domain-search-fx .icon {
  right: 1.5rem !important;
  font-size: 1.35rem !important;
}

#hero-home .inp-group .icon {
  right: 3rem;
  font-size: 2.2rem;
}




#hero-home .search-form button {
  position: relative;
  overflow: hidden;
  font-size: 20px;
  font-weight: bold;
  white-space: nowrap;
  cursor: pointer;
  line-height: 2.3;
  padding: 0.7rem 2rem;
  border-radius: 10px;

  border: 1px solid rgba(0, 0, 0, 0.15);
  background: linear-gradient(145deg, #ffffff, #f2f2f2);
  color: #000;

  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

#hero-home .search-form button::before {
  content: "";
  position: absolute;
  top: 0;
  left: -75%;
  width: 50%;
  height: 100%;
  background: linear-gradient(
    120deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.6) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: skewX(-25deg);
}


#hero-home .search-form button:hover {
  transform: translateY(-3px) scale(1.05);
  background: linear-gradient(145deg, #ffffff, #e6e6e6);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
}

#hero-home .search-form button:hover::before {
  animation: shine 0.9s forwards;
}

#hero-home .search-form button:active {
  transform: translateY(1px) scale(0.97);
  background: linear-gradient(145deg, #f0f0f0, #dcdcdc);
  box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.2);
}

@keyframes shine {
  0% {
    left: -75%;
  }
  100% {
    left: 125%;
  }
}


/* /////////////////// FEATURES HOME STYLES /////////////// */
/* /////////////////////////////////////////////////////// */
#features-home .card {
  border-radius: var(--radius-24);
  box-shadow: var(--drop-shadow);
  border: none;
  transition: var(--transition-bounce);
  overflow: hidden;
  position: relative;
}

#features-home .card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 5px;
  background: #000;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}

#features-home .card:hover {
  transform: translateY(-10px);
  box-shadow: var(--shadow-lg);
}

#features-home .card:hover::before {
  transform: scaleX(1);
}

#features-home .card.active {
  background-color: var(--color-primary);
}

#features-home figure {
  width: 70px;
  height: 70px;
  border-radius: var(--radius-16);
  background-color: #fff;
  padding: 5px;
  transition: all 0.4s ease;
}



#features-home .card-heading {
  font-size: 18px;
  font-weight: 700;
  transition: all 0.3s ease;
}

#features-home .card-desc {
  color: var(--color-text);
  transition: all 0.3s ease;
  font-size: 1.2rem;
}

#features-home .card.active .card-desc {
  color: var(--color-text-dark);
}

/* /////////////////// BANNER HOME STYLES /////////////// */
/* /////////////////////////////////////////////////////// */
#banner-home .container {
  border-radius: var(--radius-24);
}

#banner-home .content {
  padding: 2rem;
  border-radius: var(--radius-24);
  overflow: hidden;
  position: relative;
}

#banner-home .content::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: radial-gradient(
      3px 3px at 10% 10%,
      rgba(255, 255, 255, 0.9),
      transparent
    ),
    radial-gradient(
      2.5px 2.5px at 25% 85%,
      rgba(255, 255, 255, 0.7),
      transparent
    ),
    radial-gradient(3px 3px at 90% 70%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(3px 3px at 40% 70%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(3px 3px at 60% 20%, rgba(255, 255, 255, 0.8), transparent);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  z-index: -1;
}

#banner-home .section-heading {
  font-size: 2.8rem;
}

#banner-home .description {
  font-size: 2rem;
}

/* The slot has no width of its own, so the image renders at whatever its
   natural size is - the previous asset happened to be 563px wide and that is
   what set the layout. The replacement is supplied at 2x for high-density
   screens, which without this rule would render it at 1126px and push the
   section past the viewport. Pinned to the size the design was built around;
   max-width keeps it inside narrow columns. */
#banner-home .right-side img {
  width: 563px;
  max-width: 100%;
  height: auto;
}

#banner-home .right-side {
  position: absolute;
  bottom: 0;
  right: 0;
}

/* /////////////// HERO 3D MODEL LAYER /////////////// */
#hero-home {
  position: relative;
  overflow: hidden;
}
.hero-home-row {
  position: relative;
  min-height: clamp(540px, 44vw, 760px);
}

#hero-home .hero-copy-col {
  position: relative;
  z-index: 3;
}

#hero-home .hero-model-col {
  position: relative;
  z-index: 2;
  min-height: clamp(540px, 44vw, 760px);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  pointer-events: none;
}

#hero-home .hero-model-stage {
  position: relative;
  width: min(100%, 780px);
  height: clamp(540px, 44vw, 760px);
  margin-left: auto;
  pointer-events: none;
}

.hero-bg-model {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  background: transparent !important;
  --poster-color: transparent;
  outline: none;
  pointer-events: none;
  z-index: 0;
  display: block;
  opacity: 0;
  transition: opacity 360ms ease;
}
.hero-bg-model.is-loaded {
  opacity: 1;
}

#hero-home .hero-content {
  position: relative;
  z-index: 3;
  width: min(1700px, 90%);
  margin-left: auto;
  margin-right: auto;
}

/* Subtle gradient keeps the copy readable without turning the server into a foggy background. */
#hero-home::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    rgba(6, 10, 18, 0.58) 0%,
    rgba(6, 10, 18, 0.28) 32%,
    rgba(6, 10, 18, 0.08) 52%,
    rgba(6, 10, 18, 0) 72%
  );
  pointer-events: none;
  z-index: 1;
}
#hero-home .hero-ellipse { display: none; }

/* //////////////////// MEDIA QUERIES ///////////////// */
/* ///////////////////////////////////////////////////// */
@media (max-width: 991px) {
  #hero-home {
    min-height: 780px;
  }

  .hero-home-row {
    min-height: 0;
  }

  #hero-home .hero-model-col {
    position: absolute;
    inset: 0;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
  }

  #hero-home .hero-model-stage {
    position: relative;
    width: min(118vw, 820px);
    height: min(72vh, 620px);
    margin: 0 auto;
    opacity: 0.72;
  }

  .hero-bg-model {
    opacity: 0.74;
  }

  #hero-home::after {
    background: linear-gradient(
      180deg,
      rgba(6, 10, 18, 0.72) 0%,
      rgba(6, 10, 18, 0.24) 34%,
      rgba(6, 10, 18, 0.60) 100%
    );
  }
}

@media (max-width: 767px) {
  #hero-home {
    min-height: 720px;
  }

  #hero-home .hero-model-stage {
    width: min(128vw, 720px);
    height: min(64vh, 540px);
    opacity: 0.60;
  }

  .hero-bg-model {
    opacity: 0.60;
  }
}

@media (max-width: 575px) {
  #hero-home .hero-model-stage {
    width: min(138vw, 620px);
    height: min(56vh, 440px);
    opacity: 0.54;
  }
}



/* ==========================================================================
   Hero swap: the server drops out of frame and the results table drops into
   the space it leaves, both at the same moment. Desktop only — below 992px
   the model is a faded backdrop and the results sit in normal flow under the
   search field.

   Gravity, not depth. The table falls in with the same weight the server has
   on page load: accelerating down (the ease-in on the first segment), landing,
   then two decaying bounces. hero-3d.js does the page-load fall inside the 3D
   scene with GSAP power2.in over 1.38s; cubic-bezier(0.55, 0.085, 0.68, 0.53)
   is the CSS equivalent of that curve, so the two read as the same physics.

   #hero-home is overflow:hidden, so the departing server is clipped by the
   section edge rather than spilling over the content below.
   ========================================================================== */

@media (min-width: 992px) {
  /* Mounted into the model stage, so it costs the hero no extra height. */
  #hero-home .hero-model-stage > .domain-results {
    position: absolute;
    top: 50%;
    left: 50%;
    width: min(100%, 620px);
    max-height: min(88%, 620px);
    overflow-y: auto;
    overscroll-behavior: contain;
    margin-top: 0;
    /* Centring lives on `translate` so `transform` is free for the fall. */
    translate: -50% -50%;
    pointer-events: auto;
    z-index: 4;
    animation: none;
    scrollbar-width: thin;
    scrollbar-color: rgba(120, 170, 255, 0.5) transparent;
  }

  /* --- the table falls in ---------------------------------------------- */
  #hero-home .hero-model-stage.is-swapping > .domain-results {
    animation: domainDropIn 1150ms both;
  }

  /* Only the first search drops the table in; later ones just change data. */
  #hero-home .hero-model-stage.has-swapped > .domain-results {
    animation: none;
  }

  @keyframes domainDropIn {
    0% {
      opacity: 0;
      transform: translateY(-165%);
      /* Accelerating downward. */
      animation-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
    }
    10% {
      opacity: 1;
    }
    /* Lands. */
    50% {
      opacity: 1;
      transform: translateY(0);
      animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    }
    /* First bounce. */
    64% {
      transform: translateY(-13%);
      animation-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
    }
    78% {
      transform: translateY(0);
      animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    }
    /* Second, smaller bounce. */
    88% {
      transform: translateY(-4%);
      animation-timing-function: cubic-bezier(0.55, 0.085, 0.68, 0.53);
    }
    100% {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* --- the server drops away ------------------------------------------- */
  #hero-home .hero-model-stage.is-swapping .hero-bg-model {
    animation: serverDropOut 700ms cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
  }

  /* Once gone the canvas stays gone and stops taking hit-tests. */
  #hero-home .hero-model-stage.has-swapped .hero-bg-model {
    opacity: 0;
    visibility: hidden;
    animation: none;
  }

  @keyframes serverDropOut {
    0% {
      opacity: 1;
      transform: translateY(0);
    }
    /* Holds full opacity through most of the fall; the section edge does the
       hiding, the fade only softens the last of it. */
    72% {
      opacity: 1;
    }
    100% {
      opacity: 0;
      transform: translateY(155%);
    }
  }
}

/* Respect reduced-motion: same end state, nothing falls. */
@media (min-width: 992px) and (prefers-reduced-motion: reduce) {
  #hero-home .hero-model-stage.is-swapping > .domain-results,
  #hero-home .hero-model-stage.has-swapped > .domain-results {
    animation: none;
  }

  #hero-home .hero-model-stage.is-swapping .hero-bg-model {
    animation: none;
    opacity: 0;
    visibility: hidden;
  }
}

/* ==========================================================================
   TLD ticker — the extensions Everon actually resells, sliding under the hero.
   Each gets a face that suits it, so the band reads as a type specimen rather
   than a list. Twelve families cover 25 slots with no two neighbours alike.

   The families are loaded in one request subset to the ticker's glyphs via
   &text=, which takes each face from ~120KB to ~11KB.
   ========================================================================== */

/* #hero-home is a d-flex row, so a normal child becomes a flex item beside
   .hero-content and splits the width with it, squashing the hero. Absolute
   keeps the band out of that flow and pins it across the foot of the hero. */
.tld-ticker {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(22px, 4vh, 58px);
  z-index: 3;
  /* Match .main-container so the band lines up with the headline, the
     search field and everything below it, instead of running the full
     width of the window. left/right 0 plus a width and auto margins is
     what centres an absolutely positioned box.

     This also fixes the seam. The track is the list rendered twice and
     the animation translates it by exactly half, so the loop is only
     seamless while ONE copy is at least as wide as the visible box.
     A copy measures 2351px; at full window width that held up to about
     2350px and broke past it - measured on a 3408px viewport, a copy
     covered 2351 of it and left a 1057px hole, which is the blank
     stretch that ended with the row snapping back into place. Capped at
     the content column the box can no longer outgrow a copy. */
  width: min(1280px, 90%);
  margin-inline: auto;
  overflow: hidden;
  padding: 6px 0 2px;
  /* Fade both ends so items arrive and leave instead of popping at the edge. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 9%, #000 91%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 9%, #000 91%, transparent 100%);
}

.tld-ticker__track {
  display: flex;
  align-items: baseline;
  width: max-content;
  /* The list is rendered twice, so translating exactly half its width lands
     the copy where the original began and the loop has no seam. */
  animation: tldTickerScroll 70s linear infinite;
  will-change: transform;
}

/* Let people stop it to read a name they spotted. */
.tld-ticker:hover .tld-ticker__track {
  animation-play-state: paused;
}

@keyframes tldTickerScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.tld-ticker__item {
  flex: 0 0 auto;
  padding: 0 1.7rem;
  font-size: clamp(1.4rem, 2vw, 2.2rem);
  line-height: 1.5;
  white-space: nowrap;
  color: rgba(200, 218, 250, 0.38);
  transition: color 220ms ease;
}

.tld-ticker__item:hover {
  color: rgba(228, 240, 255, 0.92);
}

/* One family per personality. Every stack falls back to a real system face so
   a blocked or slow Google Fonts request still renders the band. */
.tld--playfair  { font-family: "Playfair Display", Georgia, serif; font-weight: 700; }
.tld--lora      { font-family: "Lora", Georgia, serif; font-weight: 700; }
.tld--dmserif   { font-family: "DM Serif Display", "Times New Roman", serif; }
.tld--abril     { font-family: "Abril Fatface", Georgia, serif; }
.tld--archivo   { font-family: "Archivo Black", Impact, sans-serif; }
.tld--righteous { font-family: "Righteous", Verdana, sans-serif; }
.tld--comfortaa { font-family: "Comfortaa", "Trebuchet MS", sans-serif; font-weight: 700; }
.tld--chakra    { font-family: "Chakra Petch", "Segoe UI", sans-serif; font-weight: 700; }
.tld--orbitron  { font-family: "Orbitron", "Segoe UI", sans-serif; font-weight: 700; }
.tld--pacifico  { font-family: "Pacifico", "Comic Sans MS", cursive; }
.tld--spacemono { font-family: "Space Mono", Consolas, monospace; font-weight: 700; }
.tld--jetbrains { font-family: "JetBrains Mono", Consolas, monospace; font-weight: 700; }

@media (max-width: 991px) {
  .tld-ticker__item {
    padding: 0 1.2rem;
    /* The band is the one piece of type a phone reads at a glance, and
       at the old floor it landed under 19px on every common handset. */
    font-size: clamp(1.35rem, 5.4vw, 1.95rem);
  }
  .tld-ticker__track { animation-duration: 48s; }
}

/* Mirrors the .main-container rule in globals.css, which widens the content
   column to 1700px on large screens. Kept in step with it by hand: if that
   value ever changes, this one has to move with it or the band stops
   lining up with the text above it. */
@media (min-width: 1600px) {
  .tld-ticker {
    width: 1700px;
    max-width: 90%;
  }
}

/* A band sliding forever is exactly what reduced-motion asks us not to do. */
@media (prefers-reduced-motion: reduce) {
  .tld-ticker__track { animation: none; }
}

/* ==========================================================================
   Mobile search mode.

   Below 992px the 3D model is a full-bleed backdrop rather than a column, so
   there is nothing to swap it with. Submitting a search collapses the hero
   copy instead: the headline and paragraph fold away, which lifts the field
   up under the fixed header and carries the results up with it. The model is
   untouched and keeps the section its full height behind everything.
   ========================================================================== */

@media (max-width: 991px) {
  /* max-height rather than height, because the real height is unknown; 400px
     clears three lines of headline or five of paragraph with room to spare. */
  #hero-home .hero-heading,
  #hero-home .hero-copy-col .description {
    max-height: 400px;
    overflow: hidden;
    transition:
      opacity 300ms ease,
      max-height 480ms cubic-bezier(0.22, 1, 0.36, 1),
      margin-bottom 480ms cubic-bezier(0.22, 1, 0.36, 1),
      transform 480ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  #hero-home.is-searched .hero-heading,
  #hero-home.is-searched .hero-copy-col .description {
    opacity: 0;
    max-height: 0;
    /* The headline carries Bootstrap's mb-4, which sets margin with !important. */
    margin-bottom: 0 !important;
    transform: translateY(-12px);
    pointer-events: none;
  }

  /* The section is d-flex align-items-center, so without this the shrunken
     copy would simply re-centre instead of rising. Bootstrap sets that
     utility with !important, hence the same here. The header is fixed and
     8rem tall, so 9rem clears it with a small gap. */
  #hero-home.is-searched {
    justify-content: flex-start;
    padding-top: 9rem;
    padding-bottom: 2.5rem;
    transition: padding-top 480ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* Results arrive with the field rather than after it has settled.
     enterSearchMode() is called from show(), so .is-searched lands in the
     same frame the panel becomes visible: the 480ms collapse above and
     this 460ms rise start together and read as one movement, the card
     pushing the headline up and off the screen. Scoped to .is-entering so
     a re-check does not replay it under a panel that never left. */
  #hero-home.is-searched .domain-results.is-entering {
    animation: domainRiseIn 460ms cubic-bezier(0.22, 1, 0.36, 1) both;
  }

  @keyframes domainRiseIn {
    from {
      opacity: 0;
      transform: translateY(24px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
}

@media (max-width: 991px) and (prefers-reduced-motion: reduce) {
  #hero-home .hero-heading,
  #hero-home .hero-copy-col .description,
  #hero-home.is-searched {
    transition: none;
  }

  #hero-home.is-searched .domain-results {
    animation: none;
  }
}

/* ==========================================================================
   Mobile: the ticker belongs under the results, not across them.

   Pinned absolutely it sits at a fixed offset from the section's foot, so on
   a phone the results panel grew down into it and the extensions slid over
   the prices. It goes back into normal flow here and lands below everything
   as its own band.

   #hero-home is d-flex, so a static child would become a flex item beside
   .hero-content and split the width. Switching the section to a column stacks
   them instead. align-items has to be stretch: the section carries Bootstrap's
   align-items-center, which in a column would squeeze both children to their
   content width rather than centring them vertically. Vertical centring is
   justify-content once the axis turns.
   ========================================================================== */

@media (max-width: 991px) {
  #hero-home {
    flex-direction: column;
    align-items: stretch !important;
    justify-content: center;
  }

  .tld-ticker {
    position: static;
    margin-top: 1.75rem;
    padding-top: 10px;
    padding-bottom: 2px;
  }
}

/* ------------------------------------------------------------------ *
 * Domain result: available at the registry, but not an extension we sell
 *
 * The row falls back to .is-taken for layout, but "taken" styling is wrong
 * here in two ways and both would mislead. The name is NOT registered, so
 * the red strike-through is a false statement about the domain; and the
 * action is a real link to our extension list, not an inert badge, so it
 * must not hover red like a rejection.
 * ------------------------------------------------------------------ */
.domain-results__row.is-unsupported .domain-results__icon {
  color: rgba(200, 214, 240, 0.45);
}

/* Undo the strike-through: nothing is wrong with this name. */
.domain-results__row.is-unsupported .domain-results__name {
  color: rgba(226, 234, 250, 0.75);
  text-decoration: none;
}

/* It is an <a href="domains.html">, so it gets a pointer and a neutral
   hover that invites the click instead of reading as an error. */
.domain-results__row.is-unsupported .domain-results__cta--muted {
  cursor: pointer;
}

.domain-results__row.is-unsupported .domain-results__cta--muted:hover {
  color: #e2eafa;
  border-color: rgba(255, 255, 255, 0.45);
}

/* ==========================================================================
   Re-checking a domain: hold the panel, do not collapse it.

   Each search used to replace the panel contents with a one-line
   "Checking ..." message, so a list of twenty results shrank to a single
   row and grew back when the answer arrived. Between two searches the box
   visibly closed and reopened.

   Keeping the previous results in place holds the height for free - the old
   content is still there, so there is nothing to measure - and a spinner
   over them says what the message said. Pairs with showLoading() in
   domain-search.js, which decides when there is anything worth holding.
   ========================================================================== */
.domain-results.is-loading {
  position: relative;
  /* Belt and braces: the entry animations are scoped to .is-entering, which
     a re-check never carries, so nothing should be running here anyway. */
  animation: none;
}

.domain-results.is-loading > * {
  opacity: 0.22;
  transition: opacity 160ms ease;
  pointer-events: none;
}

/* inset:0 with margin:auto and a fixed size centres on both axes, within
   the panel padding rather than the border box. */
.domain-results.is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid rgba(140, 190, 255, 0.22);
  border-top-color: rgba(120, 180, 255, 0.95);
  animation: domainResultsSpin 720ms linear infinite;
}

@keyframes domainResultsSpin {
  to { transform: rotate(360deg); }
}

/* A spinner reports progress rather than decorating, so it stays - but it
   stops being something that whips round at the edge of vision. */
@media (prefers-reduced-motion: reduce) {
  .domain-results.is-loading::after {
    animation-duration: 2.4s;
  }
  .domain-results.is-loading > * {
    transition: none;
  }
}

/* ==========================================================================
   Touch devices: solid panels instead of blurred ones.

   .domain-search-fx and .domain-results are the only blurs on this page, and
   both sit inside the hero directly over the Unicorn shader. backdrop-filter
   over ANIMATING content cannot be cached - the browser re-samples and
   re-blurs the region every frame the shader advances, which on a phone is
   every frame the hero is on screen.

   The panels only need to be readable over a moving background, and opacity
   does that as well as blur does. Raised from 0.35/0.55 to 0.82/0.88 so the
   text keeps the same contrast without the per-frame cost.

   Scoped to (pointer: coarse) rather than a width: a small window on a
   desktop GPU handles the blur fine, and a large tablet does not.
   ========================================================================== */
@media (pointer: coarse) {
  .domain-search-fx {
    background: rgba(10, 10, 12, 0.82);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .domain-results {
    background: rgba(10, 10, 12, 0.88);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ==========================================================================
   Pricing section: black, with the server render parallaxing behind it.

   NOT background-attachment: fixed. That is the usual way to build this and
   the wrong one here - it re-rasterises the background on every scrolled
   frame, which is the cost that was just taken out of the hero, and iOS
   ignores it entirely so a phone would not get the effect at all.

   Instead .pricing-parallax is a layer taller than the section, and only its
   transform moves (see parallax.js). Transforms are composited: no layout,
   no paint.
   ========================================================================== */
#pricing-home {
  position: relative;
  /* Keeps the layer's z-index: -1 inside this section, so it paints above
     the section's own black but never behind it. */
  isolation: isolate;
  overflow: hidden;
  background-color: #000;
}

.pricing-parallax {
  position: absolute;
  left: 0;
  right: 0;
  /* Overhang top and bottom is what the transform has to move within, so
     it sets the size of the effect. At -12% the travel worked out at 80px
     across a whole pass of the section - real, measurable, and far too
     small to read as parallax on a mostly-black image. parallax.js takes
     its distance from whatever this is, so the two cannot drift. */
  top: -28%;
  bottom: -28%;
  z-index: -1;
  /* The scrim rides with the image so text stays readable wherever the
     render happens to sit. Darkest at top and bottom, where the heading and
     the plan cards fall; lighter through the middle so the server is still
     clearly visible. */
  background-image:
    linear-gradient(
      180deg,
      rgba(0, 0, 0, 0.92) 0%,
      rgba(0, 0, 0, 0.7) 30%,
      rgba(0, 0, 0, 0.7) 68%,
      rgba(0, 0, 0, 0.94) 100%
    ),
    url("../../../../public/images/pricing-bg.webp");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  /* Hints the layer onto its own compositor layer before the first move,
     so the first scrolled frame is not the one that promotes it. */
  will-change: transform;
}

/* The section's own content must sit above the layer. */
#pricing-home > .main-container {
  position: relative;
  z-index: 1;
}

/* A phone shows a narrow slice of a wide render, so favour the subject over
   the empty black to its left. */
@media (max-width: 767px) {
  .pricing-parallax {
    background-position: 38% center;
  }
}

/* No movement requested: parallax.js returns without touching the layer, so
   it simply stays where CSS put it. Nothing further needed here, but the
   will-change hint is pointless then and costs a layer. */
@media (prefers-reduced-motion: reduce) {
  .pricing-parallax {
    will-change: auto;
  }
}

/* --------------------------------------------------------------------------
   A black section takes two things with it that were drawn for a light page.
   Both were invisible in a screenshot of the change before this was added,
   which is the only reason they were caught.

   The heading is color: var(--color-primary), and that variable is #000000 -
   black text on the black just introduced. The featured plan card is the
   same: background: var(--color-primary), so the card that is meant to draw
   the eye became a hole in the section.
   -------------------------------------------------------------------------- */
#pricing-home .section-heading {
  color: #ffffff;
}

/* Body copy that sits on the section rather than inside a card. */
#pricing-home > .main-container > .content > p {
  color: rgba(233, 240, 252, 0.78);
}

/* The recommended card needs an edge to exist against black, and a surface
   fractionally lifted off it so it reads as raised rather than cut out.
   Borrows the blue the hero panels use, so it belongs to the same family. */
#pricing-home .pricing-card.featured {
  background: linear-gradient(
    180deg,
    rgba(20, 26, 38, 0.96) 0%,
    rgba(8, 10, 16, 0.98) 100%
  );
  border: 1px solid rgba(120, 170, 255, 0.3);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
}

/* --------------------------------------------------------------------------
   Pricing on black: the switch and both plan cards, drawn for a dark section.

   Colours follow the footer, which is the site's existing dark surface:
   headings and emphasis #fff, supporting text var(--color-text-dark) which
   is #a7a7a7. Reusing them keeps the two dark areas of the site speaking the
   same language rather than inventing a third palette.
   -------------------------------------------------------------------------- */

/* The switch was a white pill with a black active button - correct on a white
   page, and the wrong way round here. Reversed: a faint recessed track, with
   the active side as the bright element. */
#pricing-home .toggle-buttons {
  background-color: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
}

#pricing-home .toggle-btn {
  color: var(--color-text-dark);
  transition: color 200ms ease;
}

#pricing-home .toggle-btn:hover {
  color: #ffffff;
}

#pricing-home .toggle-btn.active {
  background-color: #ffffff;
  color: #000000;
}

/* --- the cards ----------------------------------------------------------
   Both are dark now. The first carried Bootstrap's .bg-white, which sets the
   background with !important, so this has to as well - the id selector wins
   the specificity, the !important wins the tie.

   Neither card is pure black: against a black section that reads as a hole
   rather than a panel. A little lift, a hairline edge and a soft shadow make
   them sit on the section instead of in it.
   ------------------------------------------------------------------------ */
#pricing-home .pricing-card {
  background: linear-gradient(
    180deg,
    rgba(26, 30, 39, 0.9) 0%,
    rgba(12, 14, 19, 0.94) 100%
  ) !important;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 20px 46px rgba(0, 0, 0, 0.5);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

#pricing-home .pricing-card:hover {
  border-color: rgba(255, 255, 255, 0.18);
  box-shadow: 0 26px 56px rgba(0, 0, 0, 0.6);
}

/* The recommended plan keeps the blue of the hero panels, so the eye lands on
   it without a second colour being introduced to the page. */
#pricing-home .pricing-card.featured {
  background: linear-gradient(
    180deg,
    rgba(20, 32, 54, 0.94) 0%,
    rgba(9, 13, 22, 0.96) 100%
  ) !important;
  border-color: rgba(120, 170, 255, 0.34);
  box-shadow:
    0 0 0 1px rgba(120, 170, 255, 0.08),
    0 26px 60px rgba(0, 0, 0, 0.6),
    0 0 60px rgba(60, 120, 255, 0.1);
}

#pricing-home .pricing-card.featured:hover {
  border-color: rgba(120, 170, 255, 0.5);
}

/* --- type inside the cards --------------------------------------------- */
#pricing-home .plan-name,
#pricing-home .plan-price {
  color: #ffffff;
}

#pricing-home .plan-period,
#pricing-home .plan-description,
#pricing-home .plan-features li,
#pricing-home .pricing-card.featured .plan-period,
#pricing-home .pricing-card.featured .plan-description,
#pricing-home .pricing-card.featured li {
  color: var(--color-text-dark);
}

/* A filled white tick reads at a glance on a dark panel, where an outlined
   one disappears. Both cards get the same, so the featured one is
   distinguished by its edge and glow rather than by its list. */
#pricing-home .feature-icon.check,
#pricing-home .pricing-card.featured .feature-icon.check {
  background: #ffffff;
  color: #000000;
}

#pricing-home .feature-icon.cross {
  border-color: rgba(220, 53, 69, 0.45);
  color: rgba(220, 53, 69, 0.85);
}

/* --- the button --------------------------------------------------------- */
#pricing-home .explore-btn {
  border: 1.5px solid rgba(255, 255, 255, 0.22);
  color: #ffffff;
  background: transparent;
}

#pricing-home .explore-btn:hover {
  background: #ffffff;
  border-color: #ffffff;
  color: #000000;
}

/* Kept solid deliberately: the recommended plan gets the filled button and
   the other gets the outline, so the pair reads as primary and secondary
   rather than two equal choices. An earlier revision changed only its
   border and left a white button carrying a stray blue edge. */
#pricing-home .pricing-card.featured .explore-btn {
  background: #ffffff;
  border-color: #ffffff;
  color: #000000;
}

#pricing-home .pricing-card.featured .explore-btn:hover {
  background: rgba(255, 255, 255, 0.86);
  border-color: rgba(255, 255, 255, 0.86);
  color: #000000;
}

/* --------------------------------------------------------------------------
   The switch: make the active pill nest inside its track.

   The track had padding: 0 4px - horizontal only. So the active pill was
   inset 4px at the sides and flush against the top and bottom, and with both
   set to a 28px radius on a ~46px tall control, two full pills of identical
   height were being asked to sit one inside the other. Their curves start at
   different points, which is the corner mismatch: slivers of track showing
   past the ends of the pill.

   Even padding on all four sides, and a radius large enough that both are
   true pills, makes the two curves concentric - the pill is then simply the
   track inset by 4px everywhere.
   -------------------------------------------------------------------------- */
#pricing-home .toggle-buttons {
  padding: 4px;
  border-radius: 999px;
}

#pricing-home .toggle-btn {
  border-radius: 999px;
}

/* ==========================================================================
   FEATURES GRID - redesign

   This section sat between a hero carrying a 3D model and a shader, and a
   pricing section with a parallax render, and was four plain white boxes
   holding 75px grey line-art from the theme. It could not keep that company.

   It stays white on purpose. The page alternates dark hero, light features,
   black pricing, light again, and that rhythm is worth protecting - the job
   is to make the white deliberate, not to paint everything black.

   Nothing here draws per frame. No filters over moving content, no scroll
   handlers, no canvases: one static backdrop and transitions that only run
   on hover.
   ========================================================================== */

/* --- section backdrop ---------------------------------------------------
   A cool wash rising behind the cards plus a dot grid that fades out before
   it reaches the edges, so the white has depth without becoming a texture.
   Both are gradients - no image, no request.
   ------------------------------------------------------------------------ */
#features-home {
  position: relative;
  isolation: isolate;
  background-color: #ffffff;
}

#features-home::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(
      120% 90% at 50% 8%,
      rgba(56, 130, 255, 0.09) 0%,
      rgba(56, 130, 255, 0.03) 42%,
      rgba(255, 255, 255, 0) 72%
    );
  pointer-events: none;
}

#features-home::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: radial-gradient(rgba(16, 32, 56, 0.14) 1px, transparent 1px);
  background-size: 22px 22px;
  /* Masked so the grid is a suggestion in the middle of the section rather
     than a pattern running into the sections either side. */
  -webkit-mask-image: radial-gradient(75% 60% at 50% 45%, #000 0%, transparent 78%);
  mask-image: radial-gradient(75% 60% at 50% 45%, #000 0%, transparent 78%);
  opacity: 0.55;
  pointer-events: none;
}

/* --- the cards ----------------------------------------------------------- */
#features-home .card {
  background-color: rgba(255, 255, 255, 0.86);
  border: 1px solid rgba(16, 32, 56, 0.08);
  box-shadow: 0 2px 4px rgba(16, 32, 56, 0.03), 0 12px 28px rgba(16, 32, 56, 0.06);
}

#features-home .card:hover {
  transform: translateY(-10px);
  border-color: rgba(56, 130, 255, 0.28);
  box-shadow: 0 4px 8px rgba(16, 32, 56, 0.04), 0 22px 48px rgba(16, 32, 56, 0.12);
  background-color: #ffffff;
}

/* The hover bar was 5px of flat black. A 2px blue hairline is the same idea
   with the weight of a detail rather than a banner, and blue is the accent
   the hero already uses - not a new colour on the page. */
#features-home .card::before {
  height: 2px;
  background: linear-gradient(
    90deg,
    rgba(56, 130, 255, 0.95) 0%,
    rgba(120, 170, 255, 0.55) 100%
  );
}

/* --- the numeral --------------------------------------------------------
   A ghost 01-04 in the corner. It gives the row a sense of sequence and
   echoes the numbered chips already used further down the page. Drawn from
   a CSS counter so the markup carries no numbers to keep in order.
   ------------------------------------------------------------------------ */
#features-home .cards-container {
  counter-reset: feature;
}

#features-home .cards-container > li {
  counter-increment: feature;
}

#features-home .card > .d-flex {
  position: relative;
  z-index: 1;
}

#features-home .card::after {
  content: "0" counter(feature);
  position: absolute;
  top: 0.6rem;
  right: 1.4rem;
  z-index: 0;
  font-size: 4.6rem;
  font-weight: 800;
  line-height: 1;
  color: rgba(16, 32, 56, 0.05);
  letter-spacing: -0.04em;
  transition: color 0.4s ease;
  pointer-events: none;
}

#features-home .card:hover::after {
  color: rgba(56, 130, 255, 0.12);
}

/* --- the icon tile ------------------------------------------------------
   A dark chip on a light card, which is what ties this section to the black
   pricing block below and the hero above. The icons are Phosphor Duotone,
   the set used everywhere else on the site.
   ------------------------------------------------------------------------ */
#features-home .feature-icon-tile {
  width: 62px;
  height: 62px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 18px;
  background: linear-gradient(145deg, #1b2030 0%, #05070c 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.09),
    0 10px 22px rgba(16, 32, 56, 0.22);
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 0.4s ease;
}

#features-home .feature-icon-tile iconify-icon {
  font-size: 30px;
  color: #ffffff;
  line-height: 1;
  transition: color 0.35s ease;
}

/* The glyph picks up the same blue the tile glows with, so the hover reads
   as one movement rather than a lit box containing a white icon. Duotone
   icons draw both layers from currentColor - the secondary at reduced
   opacity - so setting the colour once carries the whole glyph. */
#features-home .card:hover .feature-icon-tile iconify-icon {
  color: #6ea8ff;
}

/* A lift and a blue glow instead of the old 180-degree spin. */
#features-home .card:hover .feature-icon-tile {
  transform: translateY(-3px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    0 14px 26px rgba(16, 32, 56, 0.26),
    0 0 26px rgba(56, 130, 255, 0.35);
}

/* --- type ---------------------------------------------------------------
   18px headings and 1.2rem body in a light grey were carrying a large share
   of the cheap impression on their own.
   ------------------------------------------------------------------------ */
#features-home .card-heading {
  font-size: 20px;
  letter-spacing: -0.01em;
  color: #101a2b;
}

#features-home .card-desc {
  font-size: 1.3rem;
  line-height: 1.65;
  color: #5b6472;
}

/* Nothing here should move for someone who asked for less motion. The lift
   and the glow are hover-only, so only the transitions need silencing. */
@media (prefers-reduced-motion: reduce) {
  #features-home .card,
  #features-home .card::before,
  #features-home .card::after,
  #features-home .feature-icon-tile,
  #features-home .feature-icon-tile iconify-icon {
    transition: none;
  }
  #features-home .card:hover {
    transform: none;
  }
  #features-home .card:hover .feature-icon-tile {
    transform: none;
  }
}

/* ==========================================================================
   The drive LEDs in the pricing background, blinking.

   WHY NOT AN ANIMATED WEBP

   WebP does support animation, so the idea was sound. It is the wrong tool
   here on three counts. The browser decodes and repaints the whole
   background region on every frame of an image animation, continuously, for
   as long as it is on screen - which is the per-frame cost this project has
   spent a lot of effort removing. There is no way to pause it off screen, no
   way to honour prefers-reduced-motion, and no way to vary the timing. And
   five lights blinking independently means a frame for every combination,
   which is a large file for a very small amount of light.

   HOW THIS WORKS INSTEAD

   Each overlay is a transparent canvas the SAME size as pricing-bg.webp,
   carrying a soft green bloom at the LED pixels - measured off the render at
   x=752, y=336/530/724/918/1106. Because the overlay shares the base image's
   dimensions, the same background-size: cover and background-position put
   the bloom exactly on the LED at every viewport, with no coordinate maths
   that could drift out of step.

   Only opacity animates, which the compositor handles without repainting.

   Grouped into three rather than one per LED: an element animating opacity
   becomes a compositor layer the size of the section, and five of those is
   more texture memory than this effect is worth on a mid-range phone. Three
   rhythms across five lights still reads as independent flicker.

   12.3 KB for all three, against the hundreds an animated background would
   have cost.
   ========================================================================== */
.pricing-leds {
  position: absolute;
  inset: 0;
  /* Identical to .pricing-parallax, which is what guarantees alignment. */
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
  opacity: 0.9;
}

.pricing-leds--a {
  background-image: url("../../../../public/images/pricing-leds-a.webp");
  animation: everonLedFlickerA 2.9s linear infinite;
}

.pricing-leds--b {
  background-image: url("../../../../public/images/pricing-leds-b.webp");
  animation: everonLedFlickerB 3.7s linear infinite;
  animation-delay: -1.4s;
}

.pricing-leds--c {
  background-image: url("../../../../public/images/pricing-leds-c.webp");
  animation: everonLedFlickerC 2.3s linear infinite;
  animation-delay: -0.7s;
}

/* Pairs of identical stops produce instant jumps rather than fades: drive
   activity snaps, it does not breathe. The three sequences are deliberately
   unlike each other, and the durations share no common factor, so the groups
   drift apart instead of settling into a visible pattern. */
@keyframes everonLedFlickerA {
  0%, 21% { opacity: 0.95; }
  22%, 27% { opacity: 0.18; }
  28%, 55% { opacity: 0.9; }
  56%, 59% { opacity: 0.3; }
  60%, 100% { opacity: 1; }
}

@keyframes everonLedFlickerB {
  0%, 12% { opacity: 0.85; }
  13%, 16% { opacity: 0.22; }
  17%, 44% { opacity: 1; }
  45%, 52% { opacity: 0.15; }
  53%, 78% { opacity: 0.92; }
  79%, 82% { opacity: 0.35; }
  83%, 100% { opacity: 0.88; }
}

@keyframes everonLedFlickerC {
  0%, 33% { opacity: 1; }
  34%, 37% { opacity: 0.25; }
  38%, 70% { opacity: 0.88; }
  71%, 74% { opacity: 0.2; }
  75%, 100% { opacity: 0.96; }
}

/* The section's own mobile framing shifts the render left; the overlays have
   to shift with it or the blooms come off their LEDs. */
@media (max-width: 767px) {
  .pricing-leds {
    background-position: 38% center;
  }
}

/* Paused by parallax.js while the section is off screen - a CSS animation
   otherwise runs for the whole life of the page, holding its compositor
   layers the entire time. */
#pricing-home.is-idle .pricing-leds {
  animation-play-state: paused;
}

/* Steady lights for anyone who asked for less motion: the server still reads
   as powered, nothing moves. */
@media (prefers-reduced-motion: reduce) {
  .pricing-leds {
    animation: none;
    opacity: 0.92;
  }
}
