- build-pages.mjs: per-locale page/partial overrides (*.en.html), i18n token
resolution with ru fallback, <html lang> + assetPrefix per locale
- UI dictionaries src/i18n/{ru,en}.json; lang-switch partial
- headers/footer/partials converted to {{i18n:…}} tokens
- root-absolute static asset refs so /en/ subpath loads css/js/vendor
- EN content: homepage (index.en.html + partials) and cookie-policy.en.html
(cookie-policy body translated via apinex.bond /v1/tools/translator)
405 lines
10 KiB
CSS
405 lines
10 KiB
CSS
/* Обвязка стенда. Сюда не пишем стили компонента — только рамки, сетки,
|
||
подписи и подложки. Стили neu-card живут в public/css/main.css. */
|
||
|
||
.lab {
|
||
max-width: 62rem;
|
||
margin: 0 auto;
|
||
padding: clamp(2rem, 5vw, 3.5rem) var(--space) 8rem;
|
||
}
|
||
|
||
.lab__head {
|
||
margin-bottom: clamp(2.5rem, 6vw, 4rem);
|
||
}
|
||
|
||
.lab__h1 {
|
||
margin: 0 0 0.5rem;
|
||
font-family: var(--font-accent);
|
||
font-size: clamp(1.8rem, 4vw, 2.5rem);
|
||
font-weight: 700;
|
||
line-height: 1.1;
|
||
}
|
||
|
||
.lab__sub {
|
||
margin: 0 0 1.25rem;
|
||
max-width: 42rem;
|
||
color: var(--text-muted);
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.lab__nav {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.4rem 0.75rem;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.lab__nav a {
|
||
color: var(--accent-text);
|
||
text-decoration: none;
|
||
border-bottom: 1px solid transparent;
|
||
}
|
||
|
||
.lab__nav a:hover {
|
||
border-bottom-color: currentColor;
|
||
}
|
||
|
||
.lab__sec {
|
||
padding-top: clamp(2rem, 5vw, 3rem);
|
||
margin-bottom: clamp(2rem, 5vw, 3rem);
|
||
scroll-margin-top: 1.5rem;
|
||
}
|
||
|
||
.lab__h2 {
|
||
margin: 0 0 0.5rem;
|
||
font-family: var(--font-accent);
|
||
font-size: clamp(1.25rem, 2.5vw, 1.6rem);
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.lab__h3 {
|
||
margin: 2.5rem 0 0.5rem;
|
||
font-family: var(--font-accent);
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
color: var(--text);
|
||
}
|
||
|
||
.lab__note {
|
||
margin: 0 0 1.5rem;
|
||
max-width: 46rem;
|
||
font-size: 0.9rem;
|
||
line-height: 1.6;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.lab__caption {
|
||
margin: 0.75rem 0 0;
|
||
font-size: 0.78rem;
|
||
line-height: 1.4;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.lab code {
|
||
font-family: var(--font);
|
||
font-size: 0.92em;
|
||
padding: 0.1em 0.35em;
|
||
border-radius: var(--radius-sm);
|
||
background: color-mix(in srgb, var(--text) 8%, transparent);
|
||
}
|
||
|
||
.lab__row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 1.5rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.lab__row--wrap .neu-card {
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
/* ── Токены ─────────────────────────────────────────────────────── */
|
||
|
||
.lab__tokens {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.lab__token {
|
||
margin: 0;
|
||
padding: 0.9rem 1rem;
|
||
border-radius: var(--radius-md);
|
||
background: var(--bg-elevated);
|
||
border: 1px solid var(--line);
|
||
}
|
||
|
||
.lab__token dt {
|
||
font-size: 0.82rem;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.lab__token dt span {
|
||
color: var(--text-muted);
|
||
margin-left: 0.35rem;
|
||
}
|
||
|
||
.lab__token dd {
|
||
margin: 0;
|
||
font-size: 0.82rem;
|
||
line-height: 1.5;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
/* ── Фоны ───────────────────────────────────────────────────────── */
|
||
|
||
.lab__bg-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
|
||
gap: 1.5rem;
|
||
}
|
||
|
||
.lab__bg-cell {
|
||
padding: 1.75rem;
|
||
border-radius: var(--radius-md);
|
||
border: 1px solid var(--line);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="page"] {
|
||
background: var(--bg);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="elevated"] {
|
||
background: var(--bg-elevated);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="surface"] {
|
||
background: var(--surface);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="accent"] {
|
||
background: var(--accent);
|
||
color: var(--on-accent, var(--text));
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="accent"] .lab__caption {
|
||
color: color-mix(in srgb, var(--on-accent, var(--text)) 78%, transparent);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="media"] {
|
||
background-image: url("/assets/template-elements-demo/img-long.webp");
|
||
background-size: cover;
|
||
background-position: center;
|
||
color: #fff;
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="media"] .lab__caption {
|
||
color: rgba(255, 255, 255, 0.85);
|
||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
|
||
}
|
||
|
||
.lab__bg-cell[data-bg="media"] .neu-card {
|
||
background: var(--surface);
|
||
color: var(--text);
|
||
}
|
||
|
||
/* ── Состояния ──────────────────────────────────────────────────── */
|
||
|
||
.lab__state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.lab__depth {
|
||
gap: 1.25rem;
|
||
}
|
||
|
||
.lab__depth .neu-card {
|
||
min-width: 150px;
|
||
}
|
||
|
||
/* ── Сетка ──────────────────────────────────────────────────────── */
|
||
|
||
.lab__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.lab__grid--3 {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
|
||
.lab__grid--tight {
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.lab__grid--3 {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* ── Переключатель стенда ───────────────────────────────────────── */
|
||
|
||
.lab-switch {
|
||
position: fixed;
|
||
right: max(1rem, env(safe-area-inset-right, 0px));
|
||
bottom: max(1rem, env(safe-area-inset-bottom, 0px));
|
||
z-index: 200;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
padding: 0.35rem;
|
||
border-radius: 999px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--line);
|
||
box-shadow: 0 8px 24px hsla(0, 0%, 0%, 0.25);
|
||
font-size: 0.78rem;
|
||
}
|
||
|
||
.lab-switch__btn {
|
||
appearance: none;
|
||
border: 0;
|
||
padding: 0.35rem 0.7rem;
|
||
border-radius: 999px;
|
||
background: transparent;
|
||
color: var(--text-muted);
|
||
font: inherit;
|
||
font-family: var(--font-accent);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.lab-switch__btn:hover {
|
||
color: var(--text);
|
||
background: color-mix(in srgb, var(--text) 8%, transparent);
|
||
}
|
||
|
||
.lab-switch__btn[aria-pressed="true"] {
|
||
background: var(--accent);
|
||
color: var(--on-accent, #fff);
|
||
}
|
||
|
||
.lab-switch__sep {
|
||
width: 1px;
|
||
height: 1.25rem;
|
||
margin: 0 0.15rem;
|
||
background: var(--line);
|
||
}
|
||
|
||
/* ── Карточка в карточке ────────────────────────────────────────── */
|
||
|
||
.lab__nest {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
/* Внешняя карточка: увеличенные поля и минимум, чтобы вместить вложенные */
|
||
.neu-card--nest {
|
||
--neu-pad: 1.75rem;
|
||
--neu-min-w: 0;
|
||
--neu-min-h: 0;
|
||
gap: 1rem;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.neu-card__nest-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1rem;
|
||
}
|
||
|
||
.neu-card__nest-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 1rem;
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.neu-card__nest-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* ── Шейдер-карточка (прототип Paper Shaders) ───────────────────── */
|
||
/* canvas шейдера — фоновый слой ВНУТРИ карточки, под контентом.
|
||
pointer-events:none — чтобы клик/hover доходили до самой карточки. */
|
||
.contact-card--shader {
|
||
overflow: hidden;
|
||
isolation: isolate;
|
||
}
|
||
|
||
.card-shader {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
border-radius: inherit;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
/* нейтральный фон-запас: если WebGL не отрисовал, карточка выглядит как
|
||
обычная neu-card (--neu-bg). Когда шейдер рисует — он перекрывает этот фон. */
|
||
background: var(--neu-bg);
|
||
}
|
||
|
||
/* ── neu-card + живой шейдер-слой под бликом ───────────────────────────────
|
||
Обычная неуморфная карточка (тени снаружи + блик) с анимированным шейдером
|
||
ВНУТРИ, под бликом. Слои (снизу вверх):
|
||
0 — .card-shader (canvas шейдера)
|
||
1 — ::before (блик --neu-glow) И ::after (внутренний объём inset-теней)
|
||
2 — контент карточки
|
||
Важно: box-shadow родителя рисуется НИЖЕ дочерних слоёв, поэтому шейдер (z:0)
|
||
перекрыл бы внутренние тени. Чтобы объём был виден ПОВЕРХ шейдера, внешние
|
||
тени (drop) оставляем на карточке, а внутренний объём (inset) выносим на
|
||
::after с z-index:1 — он рисуется поверх шейдера. */
|
||
.neu-card--shader {
|
||
background: transparent; /* фон уходит, шейдер снизу */
|
||
isolation: isolate;
|
||
/* только внешний объём (приподнятость) */
|
||
box-shadow: var(--neu-drop-1), var(--neu-drop-2), var(--neu-drop-3);
|
||
}
|
||
.neu-card--shader::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1; /* поверх шейдера (z:0) */
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: var(--neu-glow); /* блик из левого верхнего угла */
|
||
}
|
||
.neu-card--shader::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1; /* поверх шейдера, рядом с бликом */
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
/* внутренний объём (выпуклость): белая кайма сверху-слева + тёмная снизу-справа */
|
||
box-shadow:
|
||
var(--neu-in-light-1), var(--neu-in-light-2),
|
||
var(--neu-in-dark-1), var(--neu-in-dark-2);
|
||
}
|
||
.neu-card--shader > *:not(.card-shader) {
|
||
position: relative;
|
||
z-index: 2; /* контент над бликом/объёмом и шейдером */
|
||
}
|
||
|
||
.contact-card--shader .contact-card__icon,
|
||
.contact-card--shader .contact-card__body,
|
||
.contact-card--shader .contact-card__corner {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* отдельная демо-карточка шейдера */
|
||
.shader-demo-card {
|
||
position: relative;
|
||
overflow: hidden;
|
||
isolation: isolate;
|
||
width: 100%;
|
||
max-width: 360px;
|
||
min-height: 220px;
|
||
border-radius: var(--radius-card);
|
||
background: var(--neu-bg);
|
||
box-shadow: var(--neu-shadow);
|
||
}
|
||
|
||
.shader-demo-card--wide {
|
||
max-width: 420px;
|
||
min-height: 260px;
|
||
}
|
||
|
||
.shader-demo-card__label {
|
||
position: absolute;
|
||
left: 1rem;
|
||
bottom: 0.85rem;
|
||
z-index: 1;
|
||
font-family: var(--font-accent);
|
||
font-size: 0.95rem;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
|
||
pointer-events: none;
|
||
}
|