/* Landing page. The reset, type scale and .btn-primary that used to head
   this file now live in CSS/ND.css, which index.html loads first.
   overflow-x stays here — the hero's decorative overflow is this page's
   problem, not every page's. */
body {
  overflow-x: hidden;
}

#tsparticles {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

/* ---------------- Hero ---------------- */
.hero {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 700px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background-color: var(--nd-hero-tint-1);
  background-image: linear-gradient(180deg, var(--nd-hero-fade) 0%, var(--nd-bg) 100%);
  animation: hero-color-cycle 8s ease-in-out infinite;
  overflow: clip;
}

@keyframes hero-color-cycle {
  0% { background-color: var(--nd-hero-tint-1); }   /* dark purple */
  25% { background-color: var(--nd-hero-tint-2); }  /* dark blue */
  50% { background-color: var(--nd-hero-tint-3); }  /* dark cyan */
  75% { background-color: var(--nd-hero-tint-4); }  /* dark green */
  100% { background-color: var(--nd-hero-tint-1); } /* dark purple */
}

@media (prefers-reduced-motion: reduce) {
  .hero,
  .hook {
    animation: none;
  }
}

.hero-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 25px;
  padding: 10px 24px;
  flex-wrap: wrap;
}

#launchBtn {
  position: relative;
  z-index: 1;
}

/* The mark is a full-colour rainbow ring with the ND wordmark knocked out in
   white through its transparent centre, so it can't be inverted like the
   monochrome chrome is. On the bright theme the wordmark would otherwise be
   white-on-white, so --nd-logo-backing paints a disc through the ring's hole
   for it to sit on; on dark that token is transparent and nothing changes.
   Same treatment on .hook-logo below. */
.hero-logo {
  width: 235px;
  height: 235px;
  object-fit: contain;
  background-image: radial-gradient(circle at 50% 50%,
    var(--nd-logo-backing) 0 22%, transparent 23%);
}

.divider {
  width: 10px;
  height: 235px;
  background-color: var(--nd-white);
  border-radius: 10px;
  flex-shrink: 0;
}

.hero-slogan {
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  gap: 18px;
  max-width: 500px;
}

.hero-slogan h1 {
  text-align: left;
}

.supportive {
  display: flex;
  flex-direction: column;
  text-align: left;
}

.supportive h3 {
  color: var(--nd-white);
}

.supportive h3.muted {
  color: var(--nd-grey-light);
}

.scroll-arrow {
  position: absolute;
  bottom: 60px;
  display: flex;
  animation: bob 2s ease-in-out infinite;
}

@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(8px); }
}

/* ---------------- About us ---------------- */
.about-us {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 40px;
  padding: 20px 24px 0;
  max-width: 1400px;
  margin: 0 auto;
}

.descriptions {
  position: sticky;
  top: 64px;
  align-self: flex-start;
  width: 340px;
  flex-shrink: 0;
  padding: 100px 25px 25px 0;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.description-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.description-card .card-divider {
  margin-top: 10px;
  width: 95%;
  height: 3px;
  border-radius: 10px;
  background-color: var(--nd-divider-light);
  transition: background-color 0.3s ease, height 0.3s ease;
}

.description-card h3 {
  color: var(--nd-grey-light);
}

.description-card.active .card-divider {
  background-color: var(--nd-purple);
  height: 3px;
}

.description-card h3,
.description-card .card-more {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(10px);
  transition: opacity 0.3s ease, transform 0.3s ease, max-height 0.3s ease;
}

.description-card.active h3,
.description-card.active .card-more {
  max-height: 200px;
  opacity: 1;
  transform: translateY(0);
}

/* Was an <h4> before it became a real link; keeps that type so the row
   below the blurb reads exactly as it did. */
.description-card .card-more {
  display: block;
  font-family: var(--nd-font);
  font-size: 16px;
  font-weight: 400;
  line-height: 22.4px;
  letter-spacing: -0.64px;
  color: var(--nd-white);
  cursor: pointer;
  transition: opacity 0.3s ease, transform 0.3s ease, max-height 0.3s ease,
    color 0.2s ease;
}

.description-card .card-more:hover {
  color: var(--nd-action);
}

.photos {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  max-width: 1000px;
  padding-top: 60px;
}

.photo-block {
  width: 100%;
  margin-bottom: 130px;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 20px 50px var(--nd-shadow-ink-strong);
  opacity: 0.4;
  transform: scale(0.97);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.photo-block.active {
  opacity: 1;
  transform: scale(1);
}

.photo-block:last-child {
  margin-bottom: 60px;
}

.photo-block img {
  width: 100%;
  display: block;
}

/* The upcoming block has nothing to show a screenshot of, so it takes a
   photo's place in the column with the words instead. It still scrolls
   through the observer like the two images above it, which is what keeps
   the "5 upcoming" heading opposite it lighting up on cue. Sized in vw so
   it holds roughly the proportion of the screenshots it sits beneath. */
.photo-block.photo-note {
  /* Extra clearance on top of the usual 130px between blocks: this one is
     much shorter than the screenshots above it, so without it the note
     reaches the middle of the screen — and takes the heading over — while
     the portfolio screenshot has barely finished its own turn. */
  margin-top: 90px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  min-height: min(46vw, 460px);
  padding: 48px 40px;
  text-align: center;
  background-color: var(--nd-panel);
  border: 1px solid var(--nd-border-soft);
}

.photo-block.photo-note p {
  margin: 0;
  max-width: 22ch;
  font-family: "Roboto", sans-serif;
  font-weight: 400;
  font-size: clamp(24px, 3vw, 38px);
  line-height: 1.25;
  letter-spacing: -0.5px;
  color: var(--nd-text);
}

.photo-block.photo-note .card-more {
  /* Not inside a .description-card, so it never inherits that collapse —
     it is visible whenever the block is. */
  max-height: none;
  opacity: 1;
  transform: none;
  font-size: 17px;
  color: var(--nd-text-lilac);
}

.photo-block.photo-note .card-more:hover {
  color: var(--nd-action);
}

/* ---------------- Hook ---------------- */
/* Same animated tint cycle as the hero, but the hook sits between two
   page-background sections, so the gradient fades to --nd-bg at both edges
   and lets the tint glow through the middle. The negative delay starts the
   cycle half-way round, so hero and hook never show the same colour at
   the same moment. */
.hook {
  width: 100%;
  height: 100vh;
  min-height: 700px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--nd-hero-tint-1);
  background-image: linear-gradient(180deg,
    var(--nd-bg) 0%, var(--nd-hero-fade) 50%, var(--nd-bg) 100%);
  animation: hero-color-cycle 8s ease-in-out -4s infinite;
  overflow: clip;
}

/* A column now, not a row: the two halves are one row inside it (.hook-main),
   and the button is the second thing under them. That's what lets the button
   centre on the section rather than on the text column. */
.hook-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(34px, 4.5vw, 56px);
  padding: 10px 24px;
  width: 100%;
}

.hook-main {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  /* Wider than the hero's 25px, and no white bar between the halves: the words
     bring their own rule, and two bars a few hundred pixels apart was one too
     many. The gap does the separating. */
  gap: clamp(32px, 6vw, 90px);
  flex-wrap: wrap;
}

.hook-slogan {
  display: flex;
  flex-direction: column;
  gap: 30px;
  /* Room for three words on one line. The old 520px was cut for a stack of
     one word per row and would break this phrase in half. */
  max-width: 760px;
  align-items: flex-start;
  text-align: left;
}

/* The row. Its own size rather than the h1 from the type scale, and smaller
   than the stacked version was: three words side by side is twenty characters
   on one line, and 74px of that is wider than any column beside a logo. It
   wraps rather than overflows if a translation runs long. */
.hook-words {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: baseline;
  flex-wrap: wrap;
  gap: clamp(12px, 1.6vw, 26px);
  /* Room under the words for the rule, which is absolutely positioned and so
     claims no height of its own. */
  padding-bottom: 26px;
  font-size: clamp(26px, 4.2vw, 58px);
  line-height: 1.04;
  letter-spacing: -0.5px;
  text-transform: uppercase;
}

/* The rule the words sit on — all four stops end to end, so it reads as the
   spine the three fills were cut from, and left to right it runs the same way
   they do. This is what the "|" separators in the old one-line headline turned
   into. Stretched across the words rather than parked beside them: under a row
   it underlines the phrase, where beside a stack it only propped it up. */
.hook-words::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 7px;
  border-radius: 10px;
  background-image: linear-gradient(90deg,
    var(--nd-hook-tint-1), var(--nd-hook-tint-2),
    var(--nd-hook-tint-3), var(--nd-hook-tint-4));
}

/* Each word sweeps from one stop to the next, so read top to bottom the
   three of them are one continuous trip round the ring beside them. */
.hook-word {
  background-image: linear-gradient(96deg,
    var(--nd-hook-tint-1), var(--nd-hook-tint-2));
}

.hook-word:nth-child(2) {
  background-image: linear-gradient(96deg,
    var(--nd-hook-tint-2), var(--nd-hook-tint-3));
}

.hook-word:nth-child(3) {
  background-image: linear-gradient(96deg,
    var(--nd-hook-tint-3), var(--nd-hook-tint-4));
}

/* Clipping the fill to the glyphs is the whole effect, and it's also the
   part a browser can decline to do — so the fill is painted unconditionally
   above and only knocked out of the text where the clip is understood.
   Without the guard, an engine that ignores background-clip would paint
   three solid gradient bars with invisible words inside them. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .hook-word {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* Centred across the words above it rather than sitting against their left
   edge. `stretch` is what gives it the full width to centre in — the column
   sets `align-items: flex-start`, which would otherwise shrink this to the
   width of its own longest line and leave "centred" meaning nothing. No
   max-width for the same reason: the breaks are set by hand in the markup now,
   so a measure that re-broke them would only fight them. */
.hook-line {
  margin: 0;
  align-self: stretch;
  text-align: center;
  font-family: var(--nd-font);
  font-weight: 300;
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.55;
  letter-spacing: -0.4px;
  color: var(--nd-text-quiet);
}

/* The words rise in one after another when the section arrives (JS/index.js
   adds the classes). `pre-reveal` is added from script rather than shipped
   in the markup, so with JS off the stack is simply there. */
.hook-word {
  transition: opacity 0.6s ease,
    transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Drawn from the left now that it lies along the words rather than down their
   side — it arrives in the direction they're read in. */
.hook-words::before {
  transform-origin: left center;
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.5s ease;
}

.hook-words.pre-reveal .hook-word {
  opacity: 0;
  transform: translateY(20px);
}

.hook-words.pre-reveal::before {
  opacity: 0;
  transform: scaleX(0);
}

.hook-words.revealed .hook-word:nth-child(2) {
  transition-delay: 0.11s;
}

.hook-words.revealed .hook-word:nth-child(3) {
  transition-delay: 0.22s;
}

.hook-logo {
  /* Grown from 235px to hold its side against the taller word stack. */
  width: clamp(200px, 24vw, 300px);
  height: clamp(200px, 24vw, 300px);
  object-fit: contain;
  flex-shrink: 0;
  background-image: radial-gradient(circle at 50% 50%,
    var(--nd-logo-backing) 0 22%, transparent 23%);
}

/* ---------------- Footer ---------------- */
.site-footer {
  width: 100%;
  background-color: var(--nd-bg);
  padding: 50px 60px;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 40px;
  border-top: 1px solid var(--nd-border-soft);
}

.footer-start {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
}

/* White wordmark PNG — same invert trick the navbar chrome uses, so it
   turns black on the bright theme. */
.footer-logo {
  height: 40px;
  width: auto;
  object-fit: contain;
  filter: invert(var(--nd-icon-invert));
}

.social-icons {
  display: flex;
  align-items: center;
  gap: 18px;
}

.social-icons img {
  width: 28px;
  height: 28px;
  object-fit: contain;
  display: block;
}

.icon-x img {
  filter: invert(var(--nd-icon-invert));
}

.footer-note {
  color: var(--nd-white);
}

/* About-the-project column: one link per concept. Freelance and the work
   chat point into upcoming.html — that page is where they live until they
   ship. */
.footer-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.footer-nav-title {
  color: var(--nd-white);
  margin-bottom: 4px;
}

.footer-nav a {
  font-family: var(--nd-font);
  font-size: 14px;
  color: var(--nd-grey-light);
  text-decoration: none;
}

.footer-nav a:hover {
  color: var(--nd-white);
}

.footer-end {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  text-align: right;
}

.ncompany-logo {
  width: 60px;
  height: 60px;
  object-fit: contain;
}

.footer-end h4 {
  color: var(--nd-white);
  text-align: right;
}

/* Legal links (Step 33) — Privacy / Terms, on every page carrying the footer. */
.footer-legal {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}

.footer-legal a {
  color: var(--nd-grey-light);
  text-decoration: none;
}

.footer-legal a:hover {
  color: var(--nd-white);
}

.footer-legal-sep {
  color: var(--nd-grey);
}

/* ---------------- Responsive ---------------- */
@media (max-width: 900px) {
  .hero-content {
    flex-direction: column;
    text-align: center;
  }

  .hero-slogan {
    align-items: center;
    text-align: center;
  }

  .hero-slogan h1,
  .supportive {
    text-align: center;
  }

  .divider {
    width: 120px;
    height: 4px;
  }

  .about-us {
    flex-direction: column;
    align-items: center;
  }

  .descriptions {
    position: static;
    width: 100%;
    max-width: 500px;
    padding: 40px 0;
  }

  .photos {
    max-width: 100%;
  }

  /* The mark goes above the words at this width — column-reverse, because in
     the markup it follows them. */
  .hook-main {
    flex-direction: column-reverse;
    text-align: center;
  }

  .hook-slogan {
    align-items: center;
    text-align: center;
  }

  /* The rule is already under the words and already horizontal, so there is
     nothing to move here any more — only the row itself to centre. */
  .hook-words {
    justify-content: center;
  }

  .site-footer {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }

  .footer-start {
    align-items: center;
  }

  .footer-nav {
    align-items: center;
  }

  .footer-end {
    align-items: center;
    text-align: center;
  }

  .footer-end h4 {
    text-align: center;
  }
}

@media (max-width: 480px) {
  h1 { font-size: 30px; line-height: 34px; }
  h2 { font-size: 26px; line-height: 34px; }
  h3 { font-size: 18px; line-height: 25px; }

  .hero-logo,
  .hook-logo {
    width: 160px;
    height: 160px;
  }

  /* Eight icons and 60px of padding either side are wider than a phone, so the
     row ran off the right edge. It wraps instead, centred like the rest of the
     footer at this width. */
  .site-footer {
    padding: 40px 20px;
  }

  .social-icons {
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* ---------------- Page blur overlay ---------------- */
.page-blur-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}

/* The blur is split into two panels pinned to the left and right edges.
   They start at zero width and slide inward (added via the .active class
   below) until they meet in the center, so the frosted glass sweeps in
   from both sides rather than appearing all at once. The 1px overlap at
   the seam avoids a hairline of un-blurred page showing through. */
.page-blur-overlay::before,
.page-blur-overlay::after {
  content: "";
  position: absolute;
  top: 0;
  height: 100%;
  width: 0;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: width 0.45s ease-out;
}

.page-blur-overlay::before {
  left: 0;
}

.page-blur-overlay::after {
  right: 0;
}

.page-blur-overlay.active::before,
.page-blur-overlay.active::after {
  width: calc(50% + 1px);
}

.modal-content {
  position: relative;
  z-index: 1;
  width: 90%;
  max-width: 550px;
  height: 600px;
  border-radius: 16px;
  overflow: hidden;
  border: none;
  /* Hold the form back until the two blur panels have nearly met, then
     fade/scale it in on top of the finished frosted backdrop. */
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 0.3s ease 0.35s, transform 0.3s ease 0.35s;
}

.page-blur-overlay.active .modal-content {
  opacity: 1;
  transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .page-blur-overlay::before,
  .page-blur-overlay::after,
  .modal-content {
    transition: none;
  }
}

.modal-content iframe {
  width: 100%;
  height: 100%;
  border: none;
}

/* ---------------- Page cover transition (placeholder) ---------------- */
.page-cover-transition {
  position: fixed;
  inset: 0;
  z-index: 10000;
  pointer-events: none;
  overflow: hidden;
}

/* An oversized transparent oval whose box-shadow floods everything outside
   it with --nd-cover (black on dark, white on bright); shrinking the oval to
   nothing lets that curtain sweep inward. */
.cover-oval {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 200vw;
  height: 200vh;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 9999px var(--nd-cover);
  transition: width 0.6s ease-in, height 0.6s ease-in;
}

.page-cover-transition.closing .cover-oval {
  width: 0;
  height: 0;
}
