/* Афиша сезона на /poster: билеты кассы.
 *
 * Блок порождает build/patch-upcoming.mjs из custom/spectacles.json —
 * показы появляются и пропадают сами, прошедшие исчезают при пересборке.
 * Правится генератор и реестр, этот файл копируется в site/ как есть.
 *
 * Фактура и ритм взяты у печатной афиши театра: тёплая бумага в крап под
 * разделом, белая кассовая лента у билета, крупная дата на корешке,
 * зал справа, оранжевый маркер на премьере.
 */

.theater-season {
    --red: #bc0022;
    --marker: #ff8562;

    --paper: #fffdf7;   /* билет — белая кассовая лента */
    --paper-pattern: repeating-linear-gradient(0deg, rgba(120,95,55,.04) 0 1px, transparent 1px 3px);
    --backdrop: #f1ece0;
    --outline: #17150f;
    --bw: 1.5px;        /* толщина контура — участвует в расчёте вырезов */
    --ink: #221f18;     /* краска на бумаге не бывает чисто чёрной */
    --shadow: 70, 58, 40;

    --f-name: 'Montserrat', sans-serif;
    --f-mono: 'IBM Plex Mono', ui-monospace, monospace;

    padding: 18px 24px 52px;
    background-color: var(--backdrop);
    background-image: radial-gradient(rgba(0,0,0,.045) 1px, transparent 1.4px);
    background-size: 5px 5px;
    color: var(--ink);
    font-family: var(--f-name);
    box-sizing: border-box;
}
.theater-season *, .theater-season *::before { box-sizing: border-box; }

.theater-season__in { max-width: 880px; margin: 0 auto; }
.theater-season__over {
    /* Подпись должна читаться как продолжение заголовка «АФИША», поэтому
       сверху её прижимаем (уменьшенный padding раздела), а снизу отодвигаем
       от билетов. Раньше было наоборот, и она липла к билетам. */
    margin: 0 0 44px;
    text-align: center; font-family: var(--f-mono);
    font-size: 11.5px; font-weight: 500; letter-spacing: .3em; text-transform: uppercase; opacity: .6;
}
.theater-season__title {
    margin: 8px 0 32px; text-align: center;
    font-size: 38px; font-weight: 700; letter-spacing: .05em;
}
.theater-season__foot {
    margin: 30px 0 0; text-align: center;
    font-family: var(--f-mono); font-size: 11.5px; line-height: 1.7; opacity: .55;
}

/* Ряды разведены шире колонок: вырезы выступают за края билета, и при
   равном зазоре они у соседних билетов смыкались бы в цепочку. */
.theater-season__grid {
    --col-gap: 28px;
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 46px var(--col-gap);
}

/* Нечётный последний билет не оставляем прижатым влево — ставим по центру.
   Признак вешает скрипт: при свёрнутом списке «последний видимый» и
   «последний в разметке» — разные билеты, css такое не различает. */
.theater-season__grid .tkt.is-alone {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc(50% - var(--col-gap) / 2);
}

/* ---------- билет ---------- */

.tkt {
    --tear: 92px;    /* ширина корешка = положение линии отрыва */
    --notch: 10px;   /* радиус выреза */
    position: relative; display: flex; min-height: 164px;
    padding: var(--bw);
    border-radius: 10px;

    /* Маска НЕ из двух половин по высоте, а из двух полноразмерных слоёв,
       пересекаемых между собой (mask-composite). Половинки давали тонкий
       шов ровно по середине билета: на границе слоёв при растеризации
       просвечивал нижний, залитый цветом контура, — и особенно заметно это
       становилось на наведении, где добавляется filter. У полноразмерных
       слоёв границы нет вовсе, поэтому и шву взяться неоткуда.

       Билет собран из двух слоёв, чтобы получить И сквозные вырезы,
       И обводку по их дуге. Нижний слой залит цветом контура, верхний
       (::before) — бумагой и отступает внутрь на толщину контура. У обоих
       в маске дырки в одной точке, но у бумажного радиус больше на эту
       толщину — разница и есть обводка, огибающая вырез. */
    background: var(--outline);
    -webkit-mask-image:
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 0,    transparent 98%, #000 100%),
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 100%, transparent 98%, #000 100%);
    -webkit-mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-composite: source-in;
    mask-image:
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 0,    transparent 98%, #000 100%),
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 100%, transparent 98%, #000 100%);
    mask-size: 100% 100%;
    mask-repeat: no-repeat;
    mask-composite: intersect;

    /* Тень через filter, а не box-shadow: маску box-shadow не переживает —
       обрезался бы вместе с билетом. drop-shadow идёт по силуэту с вырезами. */
    filter: drop-shadow(0 1px 1.5px rgba(var(--shadow), .3));
    transition: transform .26s cubic-bezier(.34,1.4,.64,1), filter .26s ease;
}

.tkt::before {
    content: ''; position: absolute; inset: var(--bw);
    border-radius: calc(10px - var(--bw));
    background-color: var(--paper);
    background-image: var(--paper-pattern);
    -webkit-mask-image:
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(-1 * var(--bw)),   transparent 98%, #000 100%),
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(100% + var(--bw)), transparent 98%, #000 100%);
    -webkit-mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-composite: source-in;
    mask-image:
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(-1 * var(--bw)),   transparent 98%, #000 100%),
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(100% + var(--bw)), transparent 98%, #000 100%);
    mask-size: 100% 100%;
    mask-repeat: no-repeat;
    mask-composite: intersect;
    pointer-events: none;
}

/* Курсор-палец только у тех билетов, где есть куда вести. Признак вешает
   скрипт: без него билет остаётся обычным, и обмана не возникает. */
.tkt.is-clickable { cursor: pointer; }

/* Содержимое поднимаем над бумажным слоем: ::before позиционирован
   абсолютно и без этого рисуется поверх обычных потомков. */
.tkt__stub, .tkt__body { position: relative; z-index: 1; }

/* Наклон шахматкой: наклонена половина билетов, по диагонали — в нечётных
   рядах правый, в чётных левый. При двух колонках это позиции 2, 3, 6, 7,
   10, 11 и так далее, то есть 4n+2 и 4n+3.
   Сторона зависит от столбца: у левого выше левый край, у правого правый.
   В CSS положительный угол вращает по часовой стрелке, поднимая левый край;
   отрицательный поднимает правый. */
.theater-season__grid .tkt:nth-child(4n+2) { transform: rotate(-1.1deg); }  /* правый столбец */
.theater-season__grid .tkt:nth-child(4n+3) { transform: rotate(1.1deg); }   /* левый столбец */
.theater-season__grid .tkt:hover,
.theater-season__grid .tkt:nth-child(4n+2):hover,
.theater-season__grid .tkt:nth-child(4n+3):hover {
    transform: rotate(0) translateY(-5px);
    filter: drop-shadow(0 2px 2px rgba(var(--shadow), .3)) drop-shadow(0 9px 16px rgba(var(--shadow), .22));
}

/* Линия отрыва: плюс толщина контура — корешок и вырез отсчитываются от
   внутреннего края билета, а абсолютное позиционирование от внешнего. */
.tkt__perf {
    position: absolute; left: calc(var(--tear) + var(--bw)); top: var(--notch); bottom: var(--notch);
    width: 0; transform: translateX(-1px); z-index: 1;
    border-left: 2px dashed currentColor; opacity: .3;
}

.tkt__stub {
    flex: 0 0 var(--tear);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 14px 8px; text-align: center;
    background: rgba(var(--shadow), .05);
}
.tkt__day { display: block; font-size: 38px; font-weight: 700; line-height: 1; letter-spacing: -.01em; }
.tkt__month {
    display: block;
    font-family: var(--f-mono); margin-top: 5px;
    font-size: 10.5px; font-weight: 500; letter-spacing: .18em; text-transform: uppercase; opacity: .65;
}
.tkt__when {
    display: block;
    font-family: var(--f-mono); margin-top: 11px;
    font-size: 11.5px; font-weight: 500; opacity: .8; white-space: nowrap;
}

.tkt__body { flex: 1 1 auto; min-width: 0; padding: 15px 20px 14px 20px; display: flex; flex-direction: column; }

/* Слева спектакль, справа зал и возраст — как на печатной афише театра
   и как на настоящем билете, где справа печатают ряд и место. */
.tkt__row { display: flex; align-items: flex-start; gap: 14px; }
.tkt__main { display: block; flex: 1 1 auto; min-width: 0; }
.tkt__aside {
    flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end;
    gap: 9px; padding-top: 2px; text-align: right;
}
.tkt__hall {
    display: block;
    font-family: var(--f-mono);
    font-size: 9.5px; font-weight: 500; letter-spacing: .14em;
    text-transform: uppercase; line-height: 1.65; opacity: .55;
}

/* Разметка собрана на span, чтобы безопасно жить внутри вёрстки Тильды,
   поэтому блочность задаём явно: иначе подпись и название слипаются
   в одну строку, а отступы между ними не работают. */
.tkt__kind {
    display: block;
    font-family: var(--f-mono);
    font-size: 10px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
    /* Приглушаем цветом, а не opacity: opacity гасит и вложенный маркер
       премьеры, и его пришлось бы «возвращать» отдельным кеглем и яркостью —
       из-за чего подпись у премьер выбивалась из общего ряда. */
    color: rgba(34, 31, 24, .58);
    color: color-mix(in srgb, var(--ink) 58%, transparent);
}
.tkt__name { display: block; margin: 8px 0 0; font-size: 17px; font-weight: 700; line-height: 1.22; }

/* Жанр — на всю ширину тела: в левой колонке он упирался в блок с залом
   и переносился, оставляя одно слово на второй строке. */
.tkt__genre { display: block; margin: 6px 0 0; font-size: 12.5px; line-height: 1.4; opacity: .6; }

.tkt__acts { margin-top: auto; padding-top: 14px; display: flex; align-items: center; gap: 22px; }

/* Маркер премьеры — прорисовывается, когда билет попал в поле зрения. */
.tkt__mark {
    color: var(--ink);
    padding: 2px 8px; margin: -2px -4px;
    border-radius: 6px 10px 8px 12px;
    background: linear-gradient(104deg, transparent 1.5%, var(--marker) 2.5%,
                var(--marker) 97%, transparent 98%) left center / 0% 88% no-repeat;
    transition: background-size .75s cubic-bezier(.6,0,.3,1) .15s;
}
.tkt.is-drawn .tkt__mark { background-size: 100% 88%; }

.tkt__age {
    display: inline-block; padding: 1px 6px;
    border: 1px solid currentColor; border-radius: 3px;
    font-family: var(--f-mono); font-size: 10px; font-weight: 500;
    opacity: .42; vertical-align: 2px;
}

/* Продолжительность — тем же шрифтом, что зал, но без рамки: возраст на
   билете обведён как отметка, а время идёт справкой. Ставим ниже возраста,
   чтобы правая колонка читалась сверху вниз: где, кому, сколько. */
.tkt__dur {
    display: block;
    font-family: var(--f-mono);
    font-size: 9.5px; font-weight: 500; letter-spacing: .12em;
    text-transform: uppercase; line-height: 1.65; opacity: .45;
    white-space: nowrap;
}

/* Действия — подчёркнутыми ссылками, без кнопок: бумаге идёт тишина. */
.tkt__lnk {
    font-family: var(--f-mono);
    font-size: 11.5px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
    text-decoration: none; white-space: nowrap;
    /* Подчёркивание — не рамка, а полоса-градиент внизу: у border-bottom
       градиента не бывает. Полоса нарисована фоном и прижата к низу строки. */
    padding-bottom: 3px;
    background-repeat: no-repeat;
    background-size: 100% 1.5px;
    background-position: 0 100%;
    transition: color .2s, background-image .2s;
}
/* Подчёркивание у обеих ссылок оранжевое — тот же маркер, что у премьеры,
   поэтому цвет читается как «наш акцент», а не как два разных сигнала.
   Отличаются они цветом текста: «Купить» красным, «Подробнее» чёрным. */
/* Цвет текста — через !important: у Тильды в теме есть глобальное правило
   «#allrecords a { color: … }» с идентификатором, а идентификатор перебивает
   любое число классов. Без приоритета обе ссылки красились её оранжевым.
   Подчёркивание Тильда не трогает, там приоритет не нужен. */
/* «Купить» — полоса от красного слева к оранжевому справа,
   «Подробнее» — наоборот. Направления встречные, поэтому пара читается
   как одно целое, а не как два одинаковых элемента. */
.tkt__lnk_buy {
    color: var(--red) !important;
    background-image: linear-gradient(90deg, var(--red), var(--marker));
}
.tkt__lnk_more {
    color: var(--ink) !important;
    background-image: linear-gradient(90deg, var(--marker), var(--red));
}
/* При наведении градиент гаснет и полоса становится ровно красной.
   Толщину не трогаем: строка не должна дёргаться под курсором. */
.tkt__lnk:hover { background-image: linear-gradient(90deg, var(--red), var(--red)); }
.tkt__soon { font-family: var(--f-mono); font-size: 11px; opacity: .45; }

/* ---------- раскрытие ----------
   По умолчанию видно всё: если скрипт не отработает, человек не останется
   без афиши. Сворачивает и рисует кнопку сам скрипт. */
.theater-season__more { margin: 26px 0 0; text-align: center; }
.theater-season__btn {
    font-family: var(--f-mono);
    font-size: 12px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
    color: var(--ink); background: transparent;
    border: 0; border-bottom: 1.5px solid var(--red);
    padding: 4px 2px; cursor: pointer;
    transition: color .2s;
}
.theater-season__btn:hover { color: var(--red); }
.tkt.is-hidden { display: none; }


/* ---------- прошедший показ ----------
   Корешок оторван: в маску добавляется третий слой — вертикальная прорезь
   по линии отрыва. Прорезь сквозная, поэтому корешок и тело становятся
   двумя отдельными кусками бумаги, лежащими рядом, а фон виден между ними.
   У края разрыва обводки нет — у настоящего надрыва её и не бывает.
   Признак вешает скрипт, сравнив начало показа с новосибирским «сейчас». */
.tkt.is-past {
    --gap: 13px;
    opacity: .5;

    /* Край отрыва рваный, а не прямой: прорезь рисуется векторной плиткой
       13×20, повторяющейся по высоте, и вырезается из билета. Прямая линия
       читалась как машинный срез; здесь края гуляют, как у бумаги,
       надорванной рукой.

       ПОРЯДОК СЛОЁВ ВАЖЕН. Слои маски складываются снизу вверх, и у
       `subtract` в стандарте смысл «источник там, где он ВНЕ основы», то
       есть источник минус основа, а не наоборот (у старого вебкитного
       `destination-out` — наоборот). Поэтому прорезь лежит НИЖНИМ слоем и
       вычитается из неё выкружка, а не она из билета. Пока прорезь была
       сверху, в маске оставалась одна прорезь — и прошедший билет на
       странице пропадал целиком, видны были только две выкружки.
       Проверено 20.09.2026 на «Зверушкиных историях» — первом прошедшем
       показе сезона; до него прошедших билетов на афише не было ни разу. */
    -webkit-mask-image:
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 0,    transparent 98%, #000 100%),
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 100%, transparent 98%, #000 100%),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='20'%3E%3Cpath fill='%23000' d='M3 0h7l-2 5 3 5-3 5 2 5H3l2-5-3-5 3-5z'/%3E%3C/svg%3E");
    -webkit-mask-size: 100% 100%, 100% 100%, var(--gap) 20px;
    -webkit-mask-position: 0 0, 0 0, calc(var(--tear) + var(--bw) - var(--gap) / 2) 0;
    -webkit-mask-repeat: no-repeat, no-repeat, repeat-y;
    -webkit-mask-composite: source-in, source-out, source-over;
    mask-image:
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 0,    transparent 98%, #000 100%),
        radial-gradient(circle var(--notch) at calc(var(--tear) + var(--bw)) 100%, transparent 98%, #000 100%),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='20'%3E%3Cpath fill='%23000' d='M3 0h7l-2 5 3 5-3 5 2 5H3l2-5-3-5 3-5z'/%3E%3C/svg%3E");
    mask-size: 100% 100%, 100% 100%, var(--gap) 20px;
    mask-position: 0 0, 0 0, calc(var(--tear) + var(--bw) - var(--gap) / 2) 0;
    mask-repeat: no-repeat, no-repeat, repeat-y;
    mask-composite: intersect, subtract, add;
}
/* Бумага внутри обводки — тот же разрыв, тем же порядком слоёв. */
.tkt.is-past::before {
    -webkit-mask-image:
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(-1 * var(--bw)),   transparent 98%, #000 100%),
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(100% + var(--bw)), transparent 98%, #000 100%),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='20'%3E%3Cpath fill='%23000' d='M3 0h7l-2 5 3 5-3 5 2 5H3l2-5-3-5 3-5z'/%3E%3C/svg%3E");
    -webkit-mask-size: 100% 100%, 100% 100%, var(--gap) 20px;
    -webkit-mask-position: 0 0, 0 0, calc(var(--tear) - var(--gap) / 2) 0;
    -webkit-mask-repeat: no-repeat, no-repeat, repeat-y;
    -webkit-mask-composite: source-in, source-out, source-over;
    mask-image:
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(-1 * var(--bw)),   transparent 98%, #000 100%),
        radial-gradient(circle calc(var(--notch) + var(--bw)) at var(--tear) calc(100% + var(--bw)), transparent 98%, #000 100%),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='20'%3E%3Cpath fill='%23000' d='M3 0h7l-2 5 3 5-3 5 2 5H3l2-5-3-5 3-5z'/%3E%3C/svg%3E");
    mask-size: 100% 100%, 100% 100%, var(--gap) 20px;
    mask-position: 0 0, 0 0, calc(var(--tear) - var(--gap) / 2) 0;
    mask-repeat: no-repeat, no-repeat, repeat-y;
    mask-composite: intersect, subtract, add;
}
/* Линия отрыва больше не нужна: по ней уже оторвано. */
.tkt.is-past .tkt__perf { display: none; }
/* Прошедший билет никуда не ведёт. Касса прошедшего показа отвечает
   «продажа билетов закончилась», а «Вход свободный» после начала — уже
   неправда; «Подробнее» остаётся: у спектакля бывают следующие показы. */
.tkt.is-past { cursor: default; }
.tkt.is-past .tkt__lnk_buy,
.tkt.is-past .tkt__soon { display: none; }

@media (max-width: 820px) {
    .theater-season__grid { grid-template-columns: 1fr; }

    /* В одну колонку «одинокого» билета не бывает — каждый и так на всю
       ширину. А скрипт помечает is-alone по чётности, не глядя на число
       колонок, и правило выше резало такой билет до половины экрана: на
       телефоне последний при нечётном счёте («Москва — Петушки» при
       раскрытом сезоне) выходил узким, с обрезанным текстом и наехавшими
       друг на друга «Спектакль» и «Большой зал». */
    .theater-season__grid .tkt.is-alone { width: auto; justify-self: stretch; }
}
@media (max-width: 420px) {
    .tkt { --tear: 84px; }
    .tkt__day { font-size: 34px; }
    .theater-season__title { font-size: 28px; }
    .tkt__acts { flex-wrap: wrap; gap: 14px; }
    .theater-season { padding: 34px 16px 40px; }
}
