/**
 * Карусель по цилиндру с наклонённой осью.
 *
 * Вся геометрия выводится из четырёх величин: число граней, ширина грани,
 * наклон оси и перспектива. Радиус и поправка центровки считаются формулами,
 * а не подбираются. Запасные числа на случай старого браузера обязаны
 * совпадать с формулами — проверка это сверяет.
 */

.tcar-stage {
  --tcar-faces: 8;
  --tcar-w: 420px;
  --tcar-h: 280px;
  --tcar-tilt-x: 9deg;       /* наклон оси от смотрящего */
  --tcar-tilt-z: -7deg;      /* завал оси вбок */
  --tcar-perspective: 1200px;
  --tcar-return: .45s;

  /* запасные значения для восьми граней по 420 px при наклоне 9° */
  --tcar-step: 45deg;
  --tcar-radius: 507px;
  --tcar-lift: 79.31px;

  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  perspective: var(--tcar-perspective);
  perspective-origin: 50% 50%;   /* смещённая точка схода путает расчёт */
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  touch-action: pan-y;           /* прокрутку страницы не перехватываем */
}

/* CSS умеет тригонометрию: там, где умеет, геометрия считается сама */
@supports (width: calc(1px * sin(45deg))) {
  .tcar-stage {
    --tcar-step: calc(360deg / var(--tcar-faces));
    /* R = (w / 2) / tg(180° / n) — иначе грани разойдутся или наползут */
    --tcar-radius: calc(var(--tcar-w) / 2 / tan(calc(180deg / var(--tcar-faces))));
    /* наклон поднимает переднюю грань на R · sin t — столько и возвращаем */
    --tcar-lift: calc(var(--tcar-radius) * sin(var(--tcar-tilt-x)));
  }
}

.tcar-stage[data-tcar-drag] { cursor: grabbing; }
.tcar-stage:focus-visible { outline: 2.5px solid currentColor; outline-offset: -3px; }

/**
 * Поправка translateY стоит ПЕРВОЙ: она складывается с уже наклонённым
 * положением и возвращает переднюю грань в середину кадра. Наклон стоит
 * ДО rotateY — тогда барабан крутится вокруг косой оси, а не качается.
 */
.tcar-drum {
  position: relative;
  width: var(--tcar-w);
  height: var(--tcar-h);
  transform-style: preserve-3d;
  transform:
    translateY(var(--tcar-lift))
    rotateX(var(--tcar-tilt-x))
    rotateZ(var(--tcar-tilt-z))
    rotateY(var(--tcar-angle, 0deg));
  transition: transform var(--tcar-return) cubic-bezier(.2, .7, .3, 1);
}

/* пока тянут — без перехода, иначе барабан отстаёт от руки */
.tcar-stage[data-tcar-drag] .tcar-drum { transition: none; }

.tcar-face {
  position: absolute;
  inset: 0;
  border-radius: var(--tcar-radius-face, 18px);
  overflow: hidden;
  backface-visibility: hidden;   /* иначе задние грани просвечивают зеркально */
  transform:
    rotateY(calc(var(--n) * var(--tcar-step)))
    translateZ(var(--tcar-radius));
  transition: transform var(--tcar-return) cubic-bezier(.2, .7, .3, 1);
}

/**
 * Ровно к смотрящему разворачивается одна передняя грань. Когда грань
 * спереди, её собственный rotateY гасит поворот барабана, и на ней остаётся
 * ровно наклон оси. Обратный поворот в зеркальном порядке снимает его —
 * величина постоянная, одна для любой грани.
 */
.tcar-face[data-tcar-front] {
  transform:
    rotateY(calc(var(--n) * var(--tcar-step)))
    translateZ(var(--tcar-radius))
    rotateZ(calc(var(--tcar-tilt-z) * -1))
    rotateX(calc(var(--tcar-tilt-x) * -1));
}

/**
 * Свободное вращение — непрерывное или под рукой (сценарий ставит
 * data-tcar-free). Угол меняется каждый кадр, поэтому переходы сняты: с ними
 * барабан отставал бы и дёргался. Выпрямление — не переключением отметки, а
 * долей --tcar-straight от 0 до 1: грань распрямляется, подходя к середине, и
 * заваливается, уходя. Порядок обратного поворота тот же, зеркальный.
 */
.tcar-stage[data-tcar-free] .tcar-drum,
.tcar-stage[data-tcar-free] .tcar-face { transition: none; }
.tcar-stage[data-tcar-free] .tcar-face {
  transform:
    rotateY(calc(var(--n) * var(--tcar-step)))
    translateZ(var(--tcar-radius))
    rotateZ(calc(var(--tcar-tilt-z) * -1 * var(--tcar-straight, 0)))
    rotateX(calc(var(--tcar-tilt-x) * -1 * var(--tcar-straight, 0)));
}

@media (prefers-reduced-motion: reduce) {
  .tcar-drum, .tcar-face { transition: none; }
}
