:root {
  --bg-main: #000000;
  --bg-soft: rgba(12, 7, 2, 0.82);
  --bg-card: rgba(18, 11, 4, 0.9);
  --bg-card-strong: rgba(13, 8, 3, 0.96);

  --text-main: #FFF6DC;
  --text-soft: #E8DCC8;
  --text-dim: #A6957C;

  --line-soft: rgba(224, 120, 42, 0.2);
  --border-soft: rgba(224, 120, 42, 0.25);
  --border-strong: rgba(192, 48, 26, 0.5);

  --ember-gold: #FFE000;
  --ember-amber: #E0782A;
  --ember-crimson: #C0301A;
  --ember-deep: #8F2412;
  --ember-core: #FFF6DC;

  --glow-gold: 0 0 18px rgba(255, 224, 0, 0.35);
  --glow-crimson: 0 0 20px rgba(192, 48, 26, 0.35);
  --glow-amber: 0 0 20px rgba(255, 224, 0, 0.22);

  /* Heat rising: crimson at the base, gold at the terminal, matching the logo's own
     bottom-to-top ramp. Gold is the payoff colour and stays at the far end of every run. */
  --gradient-brand: linear-gradient(90deg, #C0301A, #E0782A, #FFC24A, #FFE000);
  --gradient-brand-soft: linear-gradient(135deg, rgba(143, 36, 18, 0.14), rgba(192, 48, 26, 0.12), rgba(224, 120, 42, 0.12), rgba(255, 224, 0, 0.1));
  --gradient-text: linear-gradient(90deg, #C0301A, #E0782A, #FFC24A, #FFE000);
}

html {
  background:
    radial-gradient(circle at top left, rgba(255, 224, 0, 0.08), transparent 25%),
    radial-gradient(circle at top right, rgba(192, 48, 26, 0.08), transparent 28%),
    radial-gradient(circle at bottom center, rgba(143, 36, 18, 0.05), transparent 30%),
    var(--bg-main);
  scroll-behavior: smooth;
}

body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: scroll;
  color: var(--text-main);
  cursor: url("./icons/cursor-yellow.webp"), auto;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

::-webkit-scrollbar {
  width: 0;
  background: transparent;
}

a {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
}

.webgl {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 1;
  /* Dimmed so the foreground copy stays readable over the moving 3D scene. Tunable: 1 = full
     brightness, lower = quieter background. */
  opacity: 0.72;
}

nav {
  position: fixed;
  width: 100%;
  /* Symmetric, compact padding. The bar must not be sized by the oversized mark: with
     `align-items: center` the mark is centred and overflows the bar (see `.logoMark`'s negative
     block margins) instead of stretching this fixed, full-width bar down over the hero. */
  padding: 22px 15vw;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: "Playfair Display", serif;
  font-size: 16px;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.98) 48%,
    rgba(0, 0, 0, 0.72) 72%,
    rgba(0, 0, 0, 0) 100%
  );
  backdrop-filter: blur(6px);
  z-index: 99;
}

.nav_hidden {
  pointer-events: none;
}

.logo {
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  text-decoration: none;
  font-family: "Josefin Sans", sans-serif;
  font-weight: 700;
  font-size: 34px;
  letter-spacing: 1px;
  color: var(--text-main);
  text-shadow: 0 0 12px rgba(192, 48, 26, 0.15);
  z-index: 100;
}

.logo:hover {
  color: #ffffff;
  text-shadow: 0 0 18px rgba(255, 224, 0, 0.4), 0 0 24px rgba(192, 48, 26, 0.28);
}

.logo:active {
  transform: scale(0.97);
}

.logo::after {
  content: "";
  position: absolute;
  top: 28px;
  left: 0;
  height: 2px;
  width: 0;
  border-radius: 10px;
  background: var(--gradient-brand);
  box-shadow: var(--glow-gold), var(--glow-crimson);
  transition: width 0.6s ease-out;
}

.logo:hover::after {
  width: 188px;
}

/* THE ASCENT — intro runs once over 2s, then a slow loop.
   0.10-0.60s  edges draw bottom-to-terminal
   0.65-1.20s  nodes spring-pop in ascending order, terminal last and largest
   1.20-1.90s  wordmark letters rise
   2s+         gentle float, terminal halo breathes
   Everything in the loop is transform/opacity only, so it stays on the compositor and
   costs no main-thread time. The dashoffset draw does repaint, but it runs once. */

/* -- edges ------------------------------------------------------------------ */
.logoEdges line {
  /* pathLength="1" in the markup normalises every segment, so one dash value fits all. */
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: logoEdgeDraw 0.15s ease-out forwards;
}

.logoEdges line:nth-child(1) { animation-delay: 0.10s; }
.logoEdges line:nth-child(2) { animation-delay: 0.15s; }
.logoEdges line:nth-child(3) { animation-delay: 0.20s; }
.logoEdges line:nth-child(4) { animation-delay: 0.25s; }
.logoEdges line:nth-child(5) { animation-delay: 0.30s; }
.logoEdges line:nth-child(6) { animation-delay: 0.35s; }
.logoEdges line:nth-child(7) { animation-delay: 0.40s; }
.logoEdges line:nth-child(8) { animation-delay: 0.45s; }

@keyframes logoEdgeDraw {
  to { stroke-dashoffset: 0; }
}

/* -- nodes ------------------------------------------------------------------ */
.logoNodes circle,
.logoCore {
  /* Without fill-box the scale would pivot on the SVG origin and fling the node across
     the canvas instead of popping in place. */
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0);
  animation: logoNodePop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

.logoNodes circle:nth-child(1) { animation-delay: 0.65s; }
.logoNodes circle:nth-child(2) { animation-delay: 0.70s; }
.logoNodes circle:nth-child(3) { animation-delay: 0.75s; }
.logoNodes circle:nth-child(4) { animation-delay: 0.80s; }
.logoNodes circle:nth-child(5) { animation-delay: 0.85s; }
.logoNodes circle:nth-child(6) { animation-delay: 0.90s; }
.logoNodes .logoTerminal { animation-delay: 1s; animation-duration: 0.3s; }
.logoCore { animation-delay: 1.1s; animation-duration: 0.3s; }

@keyframes logoNodePop {
  to { transform: scale(1); }
}

/* -- terminal halo ---------------------------------------------------------- */
.logoHalo {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation:
    logoHaloIgnite 0.5s ease-out 1.05s forwards,
    logoHaloBreathe 6.5s ease-in-out 2s infinite;
}

@keyframes logoHaloIgnite {
  from { opacity: 0; transform: scale(0.6); }
  to { opacity: 0.8; transform: scale(1); }
}

@keyframes logoHaloBreathe {
  0%, 100% { opacity: 0.7; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.07); }
}

/* -- wordmark --------------------------------------------------------------- */
.logoName {
  opacity: 0;
  transform: translateY(0.35em);
  animation: logoLetterRise 0.3s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.logoName:nth-of-type(1) { animation-delay: 1.20s; }
.logoName:nth-of-type(2) { animation-delay: 1.25s; }
.logoName:nth-of-type(3) { animation-delay: 1.30s; }
.logoName:nth-of-type(4) { animation-delay: 1.35s; }
.logoName:nth-of-type(5) { animation-delay: 1.40s; }
.logoName:nth-of-type(6) { animation-delay: 1.45s; }
.logoName:nth-of-type(7) { animation-delay: 1.50s; }
.logoName:nth-of-type(8) { animation-delay: 1.55s; }
.logoName:nth-of-type(9) { animation-delay: 1.60s; }
.logoName:nth-of-type(10) { animation-delay: 1.65s; }
.logoName:nth-of-type(11) { animation-delay: 1.70s; }
.logoName:nth-of-type(12) { animation-delay: 1.75s; }

@keyframes logoLetterRise {
  to { opacity: 1; transform: translateY(0); }
}

@keyframes logoFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-2px); }
}

.navBig {
  display: flex;
  gap: 30px;
  transition: opacity 0.5s ease;
}

.nav_hidden .navBig {
  opacity: 0;
}

/* The About link routes back to the hero (which now serves as the About). It stays hidden
   while the hero is on screen (nav.at-top) and fades in once the visitor scrolls past it,
   giving them a way back to the intro. */
.navAbout {
  transition: opacity 0.4s ease, transform 0.4s ease;
}

nav.at-top .navAbout {
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
}

.navEach {
  position: relative;
  text-decoration: none;
  color: var(--text-soft);
  transition: color 0.25s ease, text-shadow 0.25s ease;
}

.navEach::before {
  content: ".";
  font-family: "Josefin Sans", sans-serif;
  font-size: 28px;
  line-height: 1px;
  padding-right: 2px;
  opacity: 0;
  color: var(--ember-gold);
  text-shadow: 0 0 12px rgba(255, 224, 0, 0.55);
}

.navEach:hover {
  color: #ffffff;
  text-shadow: 0 0 18px rgba(255, 224, 0, 0.28);
  font-weight: 700;
}

.navEach:hover::before {
  opacity: 1;
}

.navEach:active {
  font-size: 14px;
}

#navLink1::after,
#navLink2::after,
#navLink3::after,
#navLink4::after {
  content: "";
  position: absolute;
  top: 22px;
  left: 4px;
  height: 2px;
  width: 0;
  border-radius: 10px;
  background: var(--gradient-brand);
  box-shadow: var(--glow-gold);
  transition: width 0.5s ease-out;
}

#navLink1.is-on::after {
  width: 46px;
}

#navLink2.is-on::after {
  width: 92px;
}

#navLink3.is-on::after {
  width: 72px;
}

#navLink4.is-on::after {
  width: 60px;
}

#navLink5.is-on::before,
#navLink6.is-on::before,
#navLink7.is-on::before,
#navLink8.is-on::before {
  opacity: 1;
}

.navSmall {
  display: none;
  transition: opacity 0.5s ease;
}

.nav_hidden .navSmall {
  opacity: 0;
}

.hamburger {
  z-index: 100;
}

.line {
  width: 25px;
  height: 2px;
  display: block;
  margin: 6px auto;
  background: linear-gradient(90deg, var(--ember-gold), var(--ember-crimson));
  box-shadow: 0 0 8px rgba(224, 120, 42, 0.35);
  transition: all 0.3s ease-in-out;
}

.hamburger:hover {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
}

.hamburger.is-active .line:nth-child(1) {
  width: 20px;
  translate: -7px 12px;
  rotate: 45deg;
}

.hamburger.is-active .line:nth-child(2) {
  opacity: 0;
}

.hamburger.is-active .line:nth-child(3) {
  opacity: 0;
}

.hamburger.is-active .line:nth-child(4) {
  width: 20px;
  translate: 6px -12px;
  rotate: -45deg;
}

.subMenu {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  width: 80px;
  padding: 75px 15vw 100px 80vw;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.97) 70%,
    rgba(0, 0, 0, 0.72) 90%,
    rgba(0, 0, 0, 0) 100%
  );
  flex-direction: column;
  align-items: flex-end;
  gap: 16px;
  backdrop-filter: blur(8px);
}

main {
  height: 100vh;
  overflow-x: hidden;
  overflow-y: scroll;
  /* `mandatory` forces the scroller to rest on a snap point and nowhere else. Sections are
     height: fit-content, so on a short viewport (laptop, phone in landscape) they grow past
     100vh and the overflowing pixels become unreachable, which reads as the page fighting
     the scroll. `proximity` keeps the section-to-section feel but lets a tall section be
     read all the way through. */
  scroll-snap-type: y proximity;
}

section {
  display: flex;
  flex-direction: column;
  height: fit-content;
  min-height: 100vh;
  position: relative;
  font-family: "Roboto Slab", serif;
  color: var(--text-main);
  margin: 0 15vw 0 15vw;
  z-index: 90;
  /* Identical to `center` while a section is exactly 100vh; differs only when one overflows,
     where `start` keeps the top edge anchored so the rest stays scrollable. */
  scroll-snap-align: start;
  /* Force a stop at every section boundary, even mid-fling. Without this, an upward flick from
     the last section coasts past the intermediate snap points (proximity never forces a stop)
     and lands at the very top. `always` makes scrolling up move one section at a time, matching
     the section-by-section feel scrolling down already has. */
  scroll-snap-stop: always;
}

#section1 {
  justify-content: flex-end;
}

#section2 {
  justify-content: center;
}

#section3,
#section4 {
  justify-content: flex-start;
}

#section5 {
  align-items: center;
  justify-content: flex-start;
  text-align: center;
}

.sec1Tit {
  flex-wrap: wrap;
  font-size: 64px;
  line-height: 1.05;
  color: #FFFBF2;
  text-shadow:
    0 0 12px rgba(255, 224, 0, 0.18),
    0 0 24px rgba(192, 48, 26, 0.18);
}

.sec1Name {
  margin-top: -10px;
  flex-wrap: wrap;
  color: var(--text-main);
  font-size: 44px;
  line-height: 42px;
  text-shadow:
    0 0 20px rgba(0, 0, 0, 0.85),
    0 0 30px rgba(0, 0, 0, 0.8);
}

.sec1Des {
  width: fit-content;
  flex-wrap: wrap;
  font-family: "Josefin Sans", sans-serif;
  font-size: 28px;
  line-height: 1.3;
  margin: 14px 0 13px 0;
  text-shadow:
    0 0 20px rgba(0, 0, 0, 0.7),
    0 0 40px rgba(0, 0, 0, 0.65);
  background-image: var(--gradient-text);
  background-size: 220%;
  background-position: 0;
  animation: moveGradient 6s alternate infinite;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

@keyframes moveGradient {
  50% {
    background-position: 100%;
  }
}

.sec2Part {
  max-width: 54vw;
  min-height: 50px;
  margin: 20px 0 20px 0;
  border-radius: 14px;
  padding: 2px 0;
  font-family: "Josefin Sans", sans-serif;
  font-size: 19px;
  line-height: 24px;
  color: var(--text-soft);
  text-shadow: 0 0 14px rgba(0, 0, 0, 0.55);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
}

.lineSec2 {
  width: 15vw;
  height: 2px;
  margin: 20px 0 20px 0;
  border-radius: 10px;
}

.content2.lineSec2,
.content4.lineSec2 {
  background: var(--gradient-brand);
  background-size: 220%;
  background-position: 0;
  box-shadow: var(--glow-gold), var(--glow-crimson);
  animation: moveGradient 6s alternate infinite;
}

.skill {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
}

.softwareIcon {
  width: 3.6vh;
  height: 3.6vh;
  filter: drop-shadow(0 0 10px rgba(224, 120, 42, 0.3));
  transition: transform 0.25s ease, filter 0.25s ease;
}

.softwareIcon:hover {
  transform: translateY(-2px) scale(1.08);
  filter: drop-shadow(0 0 14px rgba(255, 224, 0, 0.55));
}

.secPortTit {
  flex-wrap: wrap;
  font-size: 34px;
  line-height: 36px;
  margin-top: 15vh;
  color: #FFFBF2;
  text-shadow:
    0 0 10px rgba(255, 224, 0, 0.18),
    0 0 20px rgba(192, 48, 26, 0.2);
}

.secPortDes {
  width: fit-content;
  flex-wrap: wrap;
  font-family: "Josefin Sans", sans-serif;
  font-size: 28px;
  line-height: 1.3;
  margin: 13px 0 13px 0;
  text-shadow:
    0 0 20px rgba(0, 0, 0, 0.7),
    0 0 40px rgba(0, 0, 0, 0.65);
  background-image: var(--gradient-text);
  background-size: 220%;
  background-position: 0;
  animation: moveGradient 6s alternate infinite;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.secPortFrame {
  position: relative;
  height: 50vh;
  margin: 2vh 0 3vh 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.wrapper {
  display: flex;
  transition: all 0.5s ease-out;
}

.project {
  position: relative;
  width: 30vw;
  min-height: 48vh;
  margin: 0 10px 0 10px;
  border-radius: 16px;
  border: 1px solid var(--border-soft);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.02) inset,
    0 0 18px rgba(192, 48, 26, 0.12),
    0 0 28px rgba(192, 48, 26, 0.12);
  background:
    linear-gradient(180deg, rgba(28, 18, 8, 0.92), rgba(14, 8, 3, 0.94)),
    var(--gradient-brand-soft);
  font-family: "Josefin Sans", sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0.28;
  overflow: hidden;
  /* Was `all`, which also animated the inline width set by projectResize() and forced a
     reflow for half a second on every resize. Only these three actually change. */
  transition: opacity 0.5s ease-in-out, border-color 0.5s ease-in-out, box-shadow 0.5s ease-in-out;
  /* backdrop-filter removed: the background above it is 92-94% opaque, so the blur had
     nothing visible to blur while still forcing a compositor layer per card on each scroll frame. */
}

.project.is-in {
  border: 1px solid rgba(224, 120, 42, 0.55);
  box-shadow:
    0 0 18px rgba(255, 224, 0, 0.18),
    0 0 28px rgba(192, 48, 26, 0.18);
  opacity: 1;
}

.projectTitle {
  min-height: 8vh;
  padding: 18px 18px 8px 18px;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: #FFF9EC;
  text-shadow: 0 0 10px rgba(255, 224, 0, 0.12);
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.projectImage {
  width: 100%;
  display: flex;
  justify-content: flex-start;
}

.imageWrap {
  display: flex;
  justify-content: flex-start;
  transition: all 0.5s ease-in-out;
}

.picture {
  width: 30vw;
  height: auto;
  object-fit: cover;
  filter: saturate(1.02) contrast(1.02);
}

.projectSoftware {
  width: 100%;
  padding: 22px 20px 90px 20px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}


.projectDes {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 3vh;
  margin: 10px 0 20px 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 20px;
  text-align: center;
}

.seemoreButton {
  padding: 10px 15px;
  background: rgba(4, 9, 20, 0.94);
  border: 1px solid rgba(224, 120, 42, 0.45);
  box-shadow:
    0 0 12px rgba(255, 224, 0, 0.1),
    0 0 16px rgba(192, 48, 26, 0.12);
  border-radius: 24px;
  display: flex;
  justify-content: center;
  align-items: center;
  text-decoration: none;
  z-index: 900;
  transition: all 0.25s ease;
}

.seemore {
  font-size: 18px;
  font-weight: 600;
  background-image: var(--gradient-text);
  background-size: 220%;
  background-position: 0;
  animation: moveGradient 6s alternate infinite;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.seemoreButton:hover {
  border-color: rgba(255, 224, 0, 0.9);
  box-shadow:
    0 0 14px rgba(255, 224, 0, 0.22),
    0 0 18px rgba(192, 48, 26, 0.18);
  transform: translateY(-1px);
}

.leftright {
  position: absolute;
  top: 18vh;
  width: 50px;
  height: 50px;
  border: none;
  z-index: 10;
  animation: sound 4s ease-in-out infinite;
  opacity: 0.55;
}

/* Align each carousel's arrows to the vertical centre of its card image */
#prevWebButton,
#nextWebButton {
  top: 20vh;
}

#prevGameButton,
#nextGameButton {
  top: 16vh;
}

.leftright:nth-of-type(1) {
  background: url("./icons/icon-slide-prev-100.webp?v=2") no-repeat;
  background-size: cover;
  left: 0;
  transform: translateX(calc(20vw - 30px));
}

.leftright:nth-of-type(2) {
  background: url("./icons/icon-slide-next-100.webp?v=2") no-repeat;
  background-size: cover;
  right: 0;
  transform: translateX(calc(-20vw + 30px));
}

.leftright:hover {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
  opacity: 1;
  filter: drop-shadow(0 0 12px rgba(224, 120, 42, 0.35));
}

.leftright:nth-of-type(1):active {
  background: url("./icons/icon-slide-prev-clicked-100.webp?v=2") no-repeat;
  background-size: cover;
}

.leftright:nth-of-type(2):active {
  background: url("./icons/icon-slide-next-clicked-100.webp?v=2") no-repeat;
  background-size: cover;
}

.dotIndex {
  padding: 15px 20px;
  background: rgba(5, 10, 20, 0.95);
  border: 1px solid rgba(224, 120, 42, 0.18);
  border-top: 0;
  box-shadow: 0 0 12px rgba(224, 120, 42, 0.08);
  border-radius: 0 0 30px 30px;
  display: flex;
  justify-content: center;
  gap: 10px;
}

.dot {
  width: 9px;
  height: 9px;
  border-radius: 5px;
  background: rgba(224, 120, 42, 0.55);
  transition: all 0.4s ease-in-out;
}

.dot.is-at {
  width: 30px;
  background: linear-gradient(90deg, var(--ember-gold), var(--ember-crimson));
  box-shadow: 0 0 12px rgba(224, 120, 42, 0.32);
  margin: 0 10px 0 10px;
}

.sec5Tit {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
  font-size: 40px;
  line-height: 40px;
  color: #FFFBF2;
  text-shadow:
    0 0 12px rgba(255, 224, 0, 0.18),
    0 0 20px rgba(192, 48, 26, 0.18);
  margin-top: 18vh;
}

.hand {
  width: 40px;
  height: 40px;
  animation: handWave 2.5s ease-in-out infinite;
}

@keyframes handWave {
  4% { transform: rotate(-25deg); }
  8% { transform: rotate(0deg); }
  12% { transform: rotate(-25deg); }
  16% { transform: rotate(0deg); }
  20% { transform: rotate(-25deg); }
  24% { transform: rotate(0deg); }
  28% { transform: rotate(-25deg); }
  30% { transform: rotate(0deg); }
}

.secContact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: 30px;
  gap: 20px;
  font-family: "Josefin Sans", sans-serif;
  font-size: 20px;
}

.secPhone {
  position: relative;
  width: 220px;
  height: 44px;
  border-radius: 12px;
  box-shadow:
    0 0 14px rgba(255, 224, 0, 0.18),
    0 0 20px rgba(192, 48, 26, 0.14);
  background: linear-gradient(132deg, #FFE000, #E0782A, #C0301A, #8F2412);
  background-size: 260%;
  background-position: 0;
  animation: moveGradient 8s alternate infinite;
  display: flex;
  align-items: center;
  text-decoration: none;
  color: white;
}

.secPhone::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 3px;
  width: 171px;
  height: 38px;
  translate: 0 -50%;
  border-radius: 10px;
  background-color: rgba(4, 8, 18, 0.95);
  border: 1px solid rgba(224, 120, 42, 0.15);
}

.phone {
  width: 25px;
  height: 25px;
  margin-left: 11px;
  margin-right: 18px;
  z-index: 10;
}

.secPhone:hover {
  box-shadow:
    0 0 18px rgba(255, 224, 0, 0.3),
    0 0 24px rgba(192, 48, 26, 0.2);
}

.secPhone:hover .phone {
  filter: brightness(1.2);
}

.secPhone:active {
  scale: 0.97;
}

.phoneNumber {
  z-index: 10;
}

.secMail {
  width: 220px;
  height: 44px;
  border: none;
  text-decoration: none;
  color: white;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  box-shadow:
    0 0 14px rgba(255, 224, 0, 0.18),
    0 0 20px rgba(192, 48, 26, 0.14);
  background: linear-gradient(132deg, #8F2412, #C0301A, #E0782A, #FFE000);
  background-size: 260%;
  background-position: 0;
  animation: moveGradient 8s alternate infinite;
}

.secMail:hover {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
  font-weight: 700;
  box-shadow:
    0 0 18px rgba(255, 224, 0, 0.3),
    0 0 24px rgba(192, 48, 26, 0.2);
}

.secMail:active {
  scale: 0.97;
}

.mail {
  width: 25px;
  height: 21px;
}

.secMail p {
  width: 130px;
  margin-left: 10px;
}

.secSocial {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 20px;
}

.social {
  width: 25px;
  height: 25px;
  border-radius: 5px;
}

.social_round {
  border-radius: 50%;
}

.social_youtube {
  height: 19px;
}

.social:hover {
  filter: drop-shadow(0 0 12px rgba(224, 120, 42, 0.35)) brightness(1.06);
  scale: 1.1;
}

.social:active {
  filter: brightness(1.4);
  scale: 0.92;
}

.contact_form_wrap {
  position: fixed;
  top: 0;
  left: 50%;
  width: 100vw;
  height: 100%;
  translate: -50% 0;
  background-color: rgba(2, 6, 16, 0.72);
  backdrop-filter: blur(6px);
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.contact_form_frame {
  position: relative;
  width: 80vw;
  max-width: 520px;
  height: 70vh;
  padding: 8vh 24px;
  border-radius: 24px;
  background:
    linear-gradient(180deg, rgba(20, 12, 5, 0.96), rgba(8, 5, 2, 0.97));
  border: 1px solid rgba(224, 120, 42, 0.22);
  box-shadow:
    0 0 18px rgba(255, 224, 0, 0.14),
    0 0 28px rgba(192, 48, 26, 0.16);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  gap: 30px;
}

.hidden_layer {
  display: none;
}

.close_button {
  position: absolute;
  top: 15px;
  right: 15px;
  width: 30px;
  height: 30px;
  border: none;
  z-index: 10;
  animation: sound 4s ease-in-out infinite;
  opacity: 0.5;
  background: url("./icons/icon-close-100.webp?v=2") no-repeat;
  background-size: cover;
}

.close_button:hover {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
  opacity: 1;
  scale: 1.08;
}

.close_button:active {
  scale: 0.95;
}

.contact_form {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

input,
textarea {
  width: 100%;
  background: none;
  border: solid rgba(224, 120, 42, 0.45);
  border-width: 0 0 1px 0;
  color: var(--text-main);
  font-family: "Josefin Sans", sans-serif;
  font-size: 18px;
  caret-color: #FFDF7A;
  cursor: url("./icons/pointer-yellow.webp"), pointer;
}

input {
  height: 34px;
}

textarea {
  padding: 0 0 20px 10px;
}

input::placeholder,
textarea::placeholder {
  font-size: 20px;
}

.contact_form input:nth-of-type(1)::placeholder {
  color: #FFDF7A;
}

.contact_form input:nth-of-type(2)::placeholder {
  color: #EDA45C;
}

.contact_form input:nth-of-type(3)::placeholder {
  color: #D4693A;
}

.contact_form textarea {
  resize: none;
}

.contact_form textarea::placeholder {
  color: #B8452A;
}

textarea:focus,
input:focus {
  outline: none;
  border-bottom-color: rgba(255, 224, 0, 0.8);
  box-shadow: inset 0 -1px 0 rgba(255, 224, 0, 0.6);
}

#contact_form_submit {
  margin-top: 20px;
  min-height: 38px;
  font-family: "Josefin Sans", sans-serif;
  font-size: 20px;
}

.loading_dots {
  height: 20px;
  display: none;
  align-items: center;
  gap: 6px;
}

.secMail_active p {
  display: none;
}

.secMail_active .loading_dots {
  display: flex;
}

.loading_dots span {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: white;
}

#loading_dot_1 {
  animation: loadingdots 0.8s infinite ease;
}

#loading_dot_2 {
  animation: loadingdots 0.8s infinite ease;
  animation-delay: 0.1s;
}

#loading_dot_3 {
  animation: loadingdots 0.8s infinite ease;
  animation-delay: 0.2s;
}

@keyframes loadingdots {
  0% { translate: 0 0; }
  25% { translate: 0 -5px; }
  50% { translate: 0 0; }
  100% { translate: 0 0; }
}

.contact_form_missing,
.contact_form_mail_inv,
.contact_form_mail_error {
  color: #FFE8C4;
  text-shadow: 0 0 10px rgba(255, 224, 0, 0.4);
  animation: fadeaway 3s ease forwards;
}

.contact_form_success {
  color: #FFF3D0;
  text-shadow: 0 0 10px rgba(255, 224, 0, 0.4);
  animation: fadeaway 3s ease linear;
}

@keyframes fadeaway {
  0% { opacity: 1; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}

.contact_form_hide {
  display: none;
}

.nextButton1 {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 24px;
  margin-bottom: 20vh;
}

.nextButton2 {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 30px;
  scale: 0.6;
}

.next {
  position: relative;
  width: 2px;
  height: 24px;
  border-radius: 1px;
  background: inherit;
  box-shadow: inherit;
  animation: next 2s infinite;
  rotate: -45deg;
  opacity: 0;
  border: solid 1px rgba(224, 120, 42, 0.6);
}

.next::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 24px;
  height: 2px;
  background: inherit;
  box-shadow: inherit;
}

.next:nth-of-type(1) {
  background: #FFD24A;
  box-shadow: 0 0 5px 2px rgba(255, 224, 0, 0.55);
}

.next:nth-of-type(2) {
  background: #E0782A;
  box-shadow: 0 0 5px 2px rgba(224, 120, 42, 0.55);
  translate: 0 -3px;
  animation-delay: 0.15s;
}

.next:nth-of-type(3) {
  background: #C0301A;
  box-shadow: 0 0 5px 2px rgba(143, 36, 18, 0.48);
  translate: 0 -6px;
  animation-delay: 0.3s;
}

@keyframes next {
  50% {
    opacity: 1;
  }
}

.content1,
.content2,
.content3,
.content4,
.content5,
.content6 {
  opacity: 0;
  translate: 0 80px;
}

.content1 {
  transition: opacity 1s 0.1s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99);
}

.content2 {
  transition: opacity 1s 0.3s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99) 0.2s;
}

.content3 {
  transition: opacity 1s 0.5s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99) 0.4s;
}

.content4 {
  transition: opacity 1s 0.7s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99) 0.6s;
}

.content5 {
  transition: opacity 1s 0.9s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99) 0.8s;
}

.content6 {
  transition: opacity 1s 1.1s, translate 1s cubic-bezier(0.13, 0.07, 0.26, 0.99) 1s;
}

.is-visible .content1,
.is-visible .content2,
.is-visible .content3,
.is-visible .content4,
.is-visible .content5,
.is-visible .content6 {
  opacity: 1;
  translate: 0 0;
}

footer {
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 10vh;
  padding: 3vh 15vw 0 15vw;
  display: flex;
  justify-content: flex-end;
  gap: 50px;
  background-image: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.95) 20%,
    rgba(0, 0, 0, 0.62) 70%,
    rgba(0, 0, 0, 0) 100%
  );
  backdrop-filter: blur(6px);
  box-sizing: border-box;
  z-index: 99;
}

.backToTop {
  display: none;
  width: 34px;
  height: 34px;
  margin-top: 3px;
  background: url("./icons/icon-top-arrow-64.webp") no-repeat;
  background-size: cover;
  animation: sound 5s infinite;
  opacity: 0.4;
}

.backToTop:hover {
  opacity: 1;
  filter: drop-shadow(0 0 12px rgba(224, 120, 42, 0.35));
}

.backToTop:active {
  transform: scale(0.9);
}

.endPage {
  display: flex;
  justify-content: flex-end;
  gap: 50px;
  transition: opacity 0.5s ease;
}

.nav_hidden .endPage {
  opacity: 0;
}

.sound {
  width: 40px;
  height: 40px;
  border: none;
  background-color: transparent;
  background: url("./icons/icon-mute-96.webp") no-repeat;
  background-size: cover;
  opacity: 0.65;
  animation: sound 6s infinite;
}

.sound:hover {
  cursor: url("./icons/pointer-yellow.webp"), pointer;
  filter: drop-shadow(0 0 12px rgba(224, 120, 42, 0.35));
}

.sound:active {
  transform: scale(0.9);
}

@keyframes sound {
  50% {
    opacity: 1;
  }
}

.sound_mute {
  width: 40px;
  height: 40px;
}

.copyright {
  display: none;
  font-family: "Josefin Sans", sans-serif;
  font-size: 18px;
  color: var(--text-soft);
  align-items: center;
  translate: 0 -10px;
}

.copyright_link {
  color: #FFDF7A;
  text-shadow: 0 0 8px rgba(255, 224, 0, 0.2);
}

@media only screen and (max-width: 820px) {
  .sec2Part {
    max-width: 70vw;
  }

  .lineSec2 {
    width: 30vw;
  }

  .project {
    width: 65vw;
  }

  .picture {
    width: 65vw;
  }

  .projectDes {
    width: 65vw;
  }

  .leftright:nth-of-type(1) {
    transform: translateX(-15px);
  }

  .leftright:nth-of-type(2) {
    transform: translateX(15px);
  }
}

@media only screen and (max-width: 480px) {
  nav {
    padding: 20px 10vw;
  }

  .logo {
    font-size: 24px;
  }

  /* `nav .logoMark`, not `.logoMark`: the base rule lives later in the file, so a bare
     `.logoMark` here would lose the source-order tie. The extra selector wins it back. */
  nav .logoMark {
    /* Scaled back from the desktop size, but still the prominent element of the lockup, while
       clearing a 320px screen alongside the wordmark and the 10vw nav padding on each side. */
    width: 64px;
    height: 64px;
    margin-right: 14px;
    /* Smaller overflow to match the 64px mobile mark, keeping the bar compact here too. */
    margin-top: -16px;
    margin-bottom: -16px;
  }

  .logo:hover::after {
    width: 158px;
  }

  .navBig {
    display: none;
  }

  .navSmall {
    display: flex;
  }

  .subMenu {
    padding: 75px 10vw 120px 80vw;
  }

  section {
    margin: 0 10vw 0 10vw;
  }

  #section2 {
    justify-content: flex-start;
  }

  .sec1Tit {
    font-size: 48px;
  }

  .sec1Name {
    font-size: 34px;
    line-height: 34px;
  }

  .sec1Des {
    font-size: 22px;
  }

  .sec2Part {
    max-width: 75vw;
    margin: 10px 0 10px 0;
    font-size: 16px;
    line-height: 21px;
  }

  .sec2Part:nth-of-type(1) {
    margin-top: 18vh;
  }

  .lineSec2 {
    width: 30vw;
    margin: 15px 0 15px 0;
  }

  .softwareIcon {
    width: 30px;
    height: 30px;
  }

  .secPortTit {
    margin-top: 19vh;
    font-size: 26px;
    line-height: 28px;
  }

  .secPortDes {
    font-size: 22px;
  }

  .secPortFrame {
    margin: 1vh 0 2vh 0;
  }

  .project {
    width: 80vw;
    margin: 0 8px 0 8px;
  }

  .picture {
    width: 80vw;
  }

  .projectSoftware {
    padding: 18px 20px 70px 20px;
  }

  .projectDes {
    width: 100%;
    margin: 10px 0 25px 0;
    gap: 10px;
  }

  .seemoreButton {
    padding: 7px 10px;
  }

  .seemore {
    font-size: 16px;
  }

  .leftright {
    width: 40px;
    height: 40px;
  }

  .leftright:nth-of-type(1) {
    transform: translateX(-18px);
  }

  .leftright:nth-of-type(2) {
    transform: translateX(18px);
  }

  .sec5Tit {
    font-size: 30px;
    line-height: 34px;
    margin-top: 15vh;
  }

  .hand {
    width: 30px;
    height: 30px;
  }

  .secPhone,
  .secMail {
    width: 200px;
  }

  .secPhone::after {
    width: 151px;
  }

  footer {
    padding: 3vh 10vw 0 10vw;
    gap: 20px;
  }

  .endPage {
    gap: 20px;
  }

  .sound {
    width: 30px;
    height: 30px;
  }

  .backToTop {
    width: 27px;
    height: 27px;
    margin-top: 2px;
  }

  .copyright {
    font-size: 16px;
  }
}

/* ============================================================
   Buildery Labs additions (logo mark, product cards, tech chips)
   ============================================================ */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.logo {
  align-items: center;
}

.logoMark {
  /* The mark is the identity, so it leads: sized well above the wordmark so the node graph and
     the traveling pulses read as the hero of the lockup, not a bullet before the name. */
  width: 96px;
  height: 96px;
  margin-right: 20px;
  /* Negative block margins pull the mark's margin-box back to ~48px so it doesn't inflate the
     fixed nav's height. The mark still paints at its full 96px, overflowing the compact bar a
     few px top and bottom — it leads the lockup without ballooning the bar over the hero. */
  margin-top: -24px;
  margin-bottom: -24px;
  flex: 0 0 auto;
  /* Layered ember glow — a tight gold core over a wider crimson-orange halo — gives the mark a
     lit-from-within punch so it holds attention against the dark nav. Two small drop-shadows on
     one element; the per-frame re-raster during the slow float loop is still negligible. */
  filter:
    drop-shadow(0 0 10px rgba(255, 224, 0, 0.4))
    drop-shadow(0 0 26px rgba(224, 120, 42, 0.6));
  animation: logoFloat 6.5s ease-in-out 2s infinite;
  overflow: visible;
}

.logoGap {
  margin-left: 0.4em;
}

/* Shipped (product) cards */
.shipProject .projectTitle {
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: auto;
  padding: 16px 18px 8px 18px;
}

.shipName {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: #FFF9EC;
  text-shadow: 0 0 10px rgba(255, 224, 0, 0.12);
}

.shipTag {
  font-family: "Josefin Sans", sans-serif;
  /* The subtitle is now the card's headline (the product name is gone), so it carries real
     size and a warm glow to anchor the card. */
  font-size: 21px;
  font-weight: 600;
  letter-spacing: 0.7px;
  color: #FFEBC2;
  text-shadow: 0 0 16px rgba(224, 120, 42, 0.4);
}

/* Shipped screenshots revolve on their own (cross-fade), replacing the old manual arrows.
   The first shot stays in flow so it sets the wrap height; the rest stack on top and fade in
   when .is-shown. All same-sized product screenshots, so they register exactly. */
/* The screenshot sits in a raised, rounded frame: warm keyline, layered drop-shadow for depth,
   and a gloss/vignette overlay so the flat capture reads as a physical panel. */
.shipProject .imageWrap {
  position: relative;
  width: 100%;
  /* Reserve the screenshot's own 16:10 so the frame never collapses while the lazy image
     loads, and the cross-fade layers all share one fixed box. */
  aspect-ratio: 16 / 10;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid rgba(224, 120, 42, 0.32);
  box-shadow:
    0 22px 46px rgba(0, 0, 0, 0.55),
    0 6px 16px rgba(0, 0, 0, 0.42),
    0 0 26px rgba(224, 120, 42, 0.16);
}

.shipProject .imageWrap::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 16px;
  z-index: 2;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.08),
    transparent 20%,
    transparent 74%,
    rgba(0, 0, 0, 0.3)
  );
}

/* Content-sized card with the visit link flowing right under the image — kills the big empty
   gap that opened up once the name/description/arrows were removed. */
.shipProject {
  min-height: auto;
  justify-content: flex-start;
  padding-bottom: 22px;
}

.shipProject .projectImage {
  width: 100%;
  justify-content: center;
  padding: 4px 18px 0 18px;
  box-sizing: border-box;
}

.shipProject .projectDes {
  position: static;
  height: auto;
  margin: 18px 0 2px 0;
}

/* Cards size to their content and centre against each other instead of stretching to the
   tallest, which would re-open dead space at the bottom of the shorter ones. */
.wrapper {
  align-items: center;
}

/* All shots stack absolutely in the fixed frame and cross-fade via opacity. The first shot is
   the always-on base (also covers single-image cards, which never get .is-shown); overlays
   fade in on top when the JS moves .is-shown to them. */
.shipProject .imageWrap .picture {
  position: absolute;
  top: 0;
  left: 0;
  width: 100% !important;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.9s ease-in-out;
}

.shipProject .imageWrap .picture:first-child {
  opacity: 1;
}

.shipProject .imageWrap .picture.is-shown {
  opacity: 1;
}

.shipTag.inDev {
  color: #ffd27a;
  border: 1px solid rgba(245, 181, 68, 0.42);
  background: rgba(245, 181, 68, 0.1);
  padding: 2px 12px;
  border-radius: 999px;
  box-shadow: 0 0 12px rgba(245, 181, 68, 0.12);
}

.shipProject .picture {
  /* !important defeats the inline width projectResize() writes onto every .picture, so the
     screenshot fills the padded, rounded frame instead of the raw 30vw. Height is intrinsic
     (no cover-crop) so the whole product UI shows — cropping was chopping the left of the shot. */
  width: 100% !important;
  height: auto;
  object-fit: contain;
  display: block;
}

.techRow {
  width: 100%;
  padding: 12px 12px 68px 12px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.techChip {
  font-family: "Josefin Sans", sans-serif;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.1px;
  color: #EDDFC6;
  padding: 5px 9px;
  border-radius: 9px;
  background: rgba(224, 120, 42, 0.1);
  border: 1px solid rgba(224, 120, 42, 0.24);
  /* backdrop-filter removed: chips sit on an opaque .project card, so it blurred a flat
     fill (a no-op) while promoting every chip to its own compositor layer. */
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  transition: border-color 0.25s ease, background 0.25s ease;
}

.techChip:hover {
  border-color: rgba(255, 224, 0, 0.5);
  background: rgba(224, 120, 42, 0.18);
}

.chipIcon {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
}

@media only screen and (max-width: 480px) {
  .shipProject .picture {
    /* Full screenshot, no crop, on phones too. */
    height: auto;
  }

  .techRow {
    padding: 10px 14px 62px 14px;
  }

  .techChip {
    font-size: 11.5px;
    padding: 5px 10px;
  }
}

/* The page runs ~53 infinite animations, most of them animating background-position, which
   repaints the element every frame whether or not it is on screen. A second observer in
   index.js tags sections within 300px of the viewport as .is-near; everything further out
   parks its animations so they cost nothing while the WebGL canvas has the main thread.
   The 300px margin means an animation is always already running before it scrolls in. */
section:not(.is-near),
section:not(.is-near) *::before,
section:not(.is-near) *::after,
section:not(.is-near) * {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* Negative delay so staged intros (the logo build runs to a 1.75s stagger) land on
       their end state immediately rather than sitting invisible for two seconds. */
    animation-delay: -1ms !important;
    transition-duration: 0.01ms !important;
  }

  /* SMIL animations (the logo's traveling data pulses) ignore the CSS overrides above, so
     hide them outright when the visitor has asked for reduced motion. */
  .logoPulses {
    display: none;
  }
}

/* Sticky-note toss: the centred carousel card flies off in the swipe direction (index.js adds
   .fling-left / .fling-right on touch-release, then removes it once the throw is done). Applies
   to both carousels since both cards are .project. Transform + opacity only, so it stays on the
   compositor; z-index lifts it above its neighbours mid-flight. Under prefers-reduced-motion the
   global rule above collapses the duration, so the card just advances without the throw. */
.project.fling-left {
  animation: cardFlingLeft 0.42s cubic-bezier(0.4, 0, 1, 0.5) forwards;
  z-index: 5;
}

.project.fling-right {
  animation: cardFlingRight 0.42s cubic-bezier(0.4, 0, 1, 0.5) forwards;
  z-index: 5;
}

@keyframes cardFlingLeft {
  to { transform: translateX(-135%) rotate(-13deg) scale(0.82); opacity: 0; }
}

@keyframes cardFlingRight {
  to { transform: translateX(135%) rotate(13deg) scale(0.82); opacity: 0; }
}

/* On phones/tablets the carousels are driven by swipe, not the arrows. Hide the arrows and let
   the browser own vertical panning while index.js owns the horizontal swipe (touch-action:
   pan-y). Kept in step with the 820px breakpoint the swipe handler arms at. */
@media only screen and (max-width: 820px) {
  .leftright {
    display: none;
  }

  .secPortFrame {
    touch-action: pan-y;
  }
}
