html,
body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
}

img {
  -webkit-user-drag: none;
}

a {
  color: var(--link);
  text-decoration: none;
}

a:hover {
  color: var(--link-hover);
}

a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.face:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 6px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
  }
}

/* 무대 */

.viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--bg);
  font-family: var(--font-ui);
  touch-action: none;
}

/* perspective 와 perspective-origin 의 세로값은 js/config.js 의 scene 과 맞춰야 합니다. */
.stage {
  position: absolute;
  inset: 0;
  perspective: 1500px;
  perspective-origin: 50% 46%;
}

.room,
.plane {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* 벽 */

.wall {
  position: absolute;
  pointer-events: none;
  background-size: 100% 100%, var(--grid) var(--grid), var(--grid) var(--grid);
}

.wall--back {
  inset: 0;
  transform: translateZ(calc(-1 * var(--room-depth)));
  border: 1px solid rgba(184, 212, 255, .72);
  box-shadow: inset 0 0 90px rgba(120, 165, 255, .2);
  background-image:
    radial-gradient(ellipse at 50% 52%, rgba(110, 155, 245, .2), transparent 62%),
    linear-gradient(rgba(146, 182, 252, .6) 1px, transparent 1px),
    linear-gradient(90deg, rgba(146, 182, 252, .6) 1px, transparent 1px);
  transition: filter .3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wall--floor {
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--room-depth);
  transform-origin: 50% 100%;
  transform: rotateX(90deg);
  border: 1px solid rgba(180, 208, 255, .7);
  box-shadow: inset 0 0 110px rgba(120, 165, 255, .26);
  background-image:
    linear-gradient(0deg, rgba(3, 5, 16, 0) 26%, rgba(3, 5, 16, .7) 96%),
    linear-gradient(rgba(146, 182, 252, .7) 1px, transparent 1px),
    linear-gradient(90deg, rgba(146, 182, 252, .7) 1px, transparent 1px);
}

.wall--ceiling {
  left: 0;
  right: 0;
  top: 0;
  height: var(--room-depth);
  transform-origin: 50% 0%;
  transform: rotateX(-90deg);
  border: 1px solid rgba(178, 206, 255, .5);
  box-shadow: inset 0 0 100px rgba(120, 165, 255, .16);
  background-image:
    linear-gradient(180deg, rgba(3, 5, 16, 0) 26%, rgba(3, 5, 16, .76) 96%),
    linear-gradient(rgba(138, 174, 248, .42) 1px, transparent 1px),
    linear-gradient(90deg, rgba(138, 174, 248, .42) 1px, transparent 1px);
  transition: filter .3s ease;
}

.wall--left {
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--room-depth);
  transform-origin: 0% 50%;
  transform: rotateY(90deg);
  transform-style: preserve-3d;
  border: 1px solid rgba(180, 208, 255, .66);
  box-shadow: inset 0 0 100px rgba(120, 165, 255, .22);
  background-image:
    linear-gradient(90deg, rgba(3, 5, 16, 0) 26%, rgba(3, 5, 16, .7) 96%),
    linear-gradient(rgba(144, 180, 250, .56) 1px, transparent 1px),
    linear-gradient(90deg, rgba(144, 180, 250, .56) 1px, transparent 1px);
}

.wall--right {
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--room-depth);
  transform-origin: 100% 50%;
  transform: rotateY(-90deg);
  border: 1px solid rgba(180, 208, 255, .66);
  box-shadow: inset 0 0 100px rgba(120, 165, 255, .22);
  background-image:
    linear-gradient(270deg, rgba(3, 5, 16, 0) 26%, rgba(3, 5, 16, .7) 96%),
    linear-gradient(rgba(144, 180, 250, .56) 1px, transparent 1px),
    linear-gradient(90deg, rgba(144, 180, 250, .56) 1px, transparent 1px);
  transition: filter .3s ease;
}

/* 뒷벽의 로고 */

.logos {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 34px;
}

.logo {
  display: block;
}

.logo--brand {
  width: 56vw;
  max-width: 820px;
  opacity: .9;
  filter: drop-shadow(0 0 46px rgba(150, 185, 255, .5));
}

.logo--box {
  width: 18vw;
  max-width: 270px;
  opacity: .85;
  filter: drop-shadow(0 0 30px rgba(150, 185, 255, .45));
}

/* 유리 상자 */

/*
  래퍼는 레이아웃에서 사라져야 합니다. 6면이 .plane 의 직계 형제로 남아야
  브라우저가 하나의 3D 공간 안에서 면 단위로 깊이를 정렬해 줍니다.
  여기에 position 이나 transform 을 주면 상자끼리 서로 통과합니다.
*/
.box {
  display: contents;
}

.face {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  cursor: grab;
  visibility: hidden;
  /* 물리 계산이 첫 위치를 잡으면 box-physics.js 가 켭니다 */
  background-color: var(--glass-tint);
}

/* 좌·우면 — 가로가 상자의 깊이 */
.face--left,
.face--right {
  width: var(--box-d);
  height: var(--box-h);
  border: 1px solid var(--glass-edge);
  background-image: linear-gradient(140deg, rgba(178, 202, 255, .16), rgba(140, 170, 240, .05));
}

.face--top {
  width: var(--box-w);
  height: var(--box-d);
  border: 1px solid var(--glass-edge-top);
  background-image: linear-gradient(160deg, rgba(196, 218, 255, .16), rgba(150, 180, 245, .05));
}

/* 아랫면 — 프로젝트 번호가 찍힙니다 */
.face--bottom {
  width: var(--box-w);
  height: var(--box-d);
  border: 1px solid var(--glass-edge-bottom);
  background-image: linear-gradient(200deg, rgba(160, 190, 248, .16), rgba(130, 160, 235, .05));
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 30px;
  color: var(--text-no);
}

/* 뒷면 — 테두리만 있는 맨 유리 */
.face--back {
  width: var(--box-w);
  height: var(--box-h);
  border: 1px solid var(--glass-edge-back);
  background-color: transparent;
}

/* 앞면 — 이름과 설명이 실립니다 */
.face--front {
  width: var(--box-w);
  height: var(--box-h);
  border: 1px solid var(--glass-edge-front);
  background-color: var(--glass-tint-front);
  background-image: linear-gradient(150deg, rgba(188, 208, 252, .16), rgba(150, 175, 240, .05));
  box-sizing: border-box;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  /* 한글은 기본값이면 단어 한가운데서도 줄이 바뀝니다. 띄어쓰기에서만 끊습니다. */
  word-break: keep-all;
}

/* 데모 주소가 없는 프로젝트는 글자를 살짝 흐리게 (render.js 가 붙입니다) */
.box--no-demo .card {
  opacity: .45;
  transition: opacity .32s ease;
}

/* 클릭해서 들어 올린 동안에는 제 밝기로 돌아옵니다 (box-physics.js 의 pick() 이 붙입니다) */
.box--no-demo.box--picked .card {
  opacity: 1;
}

.card__title {
  font-weight: 500;
  font-size: 34px;
  line-height: 1.08;
  color: var(--text-title);
  text-shadow: var(--glow-title);
}

.card__desc {
  font-family: var(--font-ko);
  font-weight: 300;
  font-size: 23px;
  line-height: 1.38;
  color: var(--text-desc);
  text-shadow: var(--glow-desc);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 들어 올렸을 때 상자 아래에 뜨는 버튼 */

/* 보임/숨김은 선택된 상자 하나에만 걸어야 하므로 box-physics.js 의 syncLinks() 가 직접 켭니다. */
.links {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  margin-top: 14px;
  display: flex;
  justify-content: center;
  gap: 12px;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity .22s ease, transform .22s ease;
}

.link {
  font-family: var(--font-mono);
  font-size: 15px;
  padding: 11px 20px;
  border-radius: 99px;
}

.link--open {
  border: 1px solid rgba(205, 222, 255, .6);
  background-color: rgba(9, 14, 30, .72);
  background-image: linear-gradient(160deg, rgba(186, 210, 255, .23), rgba(150, 180, 247, .32));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
}

.link--open:hover {
  background-image: linear-gradient(160deg, rgba(205, 225, 255, .42), rgba(165, 195, 252, .2));
}

.link--repo {
  border: 1px solid rgba(160, 180, 225, .4);
  color: var(--link-repo);
  background-color: rgba(9, 14, 30, .82);
  background-image: linear-gradient(160deg, rgba(160, 188, 245, .34), rgba(130, 160, 235, .24));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
}

.link--repo:hover {
  color: var(--link-hover);
  background-image: linear-gradient(160deg, rgba(180, 205, 252, .24), rgba(145, 175, 242, .1));
}