/* Campaign page: "Что добавить?" — features.snipbook.app.
   Ported from the design bundle, whose every rule was an inline style on a
   React-ish component tree. Same pixels, expressed as classes. */

:root {
  --paper: #f3f1ec;
  --ink: #1f2326;
  --muted: #5f6b73;
  --accent: #4f6a7a;
  --rule: #e2ddd2;
  --field: #fffefb;
  --field-border: #c9c3b6;
  --placeholder: #e4e0d8;

  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --sans: Montserrat, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, Menlo, Consolas, monospace;

  /* Desktop scale factor for the scatter canvas; app.js keeps it current. */
  --k: 1;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  /* The scatter canvas is a fixed 1120px wide and scaled down to fit, so a
     narrow viewport must never be able to scroll sideways past it. */
  overflow-x: hidden;
}

a { color: var(--ink); }
a:hover { color: var(--accent); }

.wrap { max-width: 1120px; margin: 0 auto; }

/* ── Masthead ─────────────────────────────────────────────────────────── */

.masthead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: clamp(28px, 6vw, 52px) clamp(20px, 5vw, 60px) 0;
}

.masthead__text { max-width: 760px; }

.masthead__title {
  font: 400 clamp(40px, 10vw, 78px)/1 var(--serif);
  letter-spacing: -0.025em;
}

.masthead__subtitle {
  margin-top: 12px;
  font: 300 clamp(22px, 5.5vw, 34px)/1.2 var(--serif);
  font-style: italic;
}

/* inline-block, not the default inline: vertical margins do nothing on a
   non-replaced inline box, so as a plain <a> the 16px below was silently
   dropped and this line sat flush against the subtitle. Not `block` either —
   that would stretch the link's hit area across the full column. */
.masthead__domain {
  display: inline-block;
  margin-top: 16px;
  font: 400 15px/1.2 var(--serif);
  letter-spacing: 0.06em;
  color: var(--muted);
  text-decoration: none;
}

.masthead__date {
  padding-top: 10px;
  font: 400 12px/1 var(--mono);
  color: var(--muted);
}

/* ── Scatter canvas ───────────────────────────────────────────────────── */

/* ONE COLUMN IS THE BASE LAYOUT, and the scatter is layered over it below.
   That order is deliberate. The scatter is a fixed 1120px canvas scaled to fit,
   and the scale factor can only be computed in script — so if it were the base,
   a visitor whose JS never ran would get an unscaled 1120px canvas inside a
   narrower frame, with a third of the composition clipped away. Stacked-first
   means the page is complete and readable on its own, and the scatter arrives
   only once script has confirmed it can be driven. */
.scatter-frame {
  position: relative;
  width: 100%;
}

.scatter {
  position: relative;
}

/* The dotted thread the cards are strung along. Decorative and pointer-inert
   so it can never swallow a click meant for a card. Only the scatter hangs
   cards from it, so it stays hidden until the scatter exists. */
.thread {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: none;
}

.spacer-top { height: 70px; }
.spacer-bottom { height: 90px; }

/* ── Card ─────────────────────────────────────────────────────────────── */

.card {
  width: 100%;
  max-width: 340px;
  margin: 0 auto 52px;
}

.card__num {
  position: absolute;
  top: -18px;
  right: 0;
  font: 400 11px/1 var(--mono);
  letter-spacing: 0.1em;
  color: #3a4148;
}

/* The lift is on the media alone, matching the bundle: the caption and vote
   button stay put, so only the image floats. */
/* Cards are sized for the desktop canvas; stacked in one column they share a
   height that tracks the viewport instead. */
.card__media {
  display: block;
  width: 100%;
  height: min(62vw, 240px);
  transition: transform 0.45s cubic-bezier(0.2, 0.8, 0.3, 1);
}

.card:hover .card__media { transform: translateY(-8px); }

img.card__media {
  object-fit: cover;
  /* The media box is a fixed height the art is cropped into; without this a
     slow-loading image would briefly collapse the card. */
  background: var(--placeholder);
}

.card__title {
  margin-top: 12px;
  font: 500 11px/1.4 var(--sans);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

/* ── Vote button ──────────────────────────────────────────────────────── */

.vote {
  margin-top: 9px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 6px 13px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  font: 400 11px/1 var(--sans);
  letter-spacing: 0.04em;
  transition: background 0.2s, color 0.2s;
}

.vote:hover { background: var(--ink); color: var(--paper); }

.vote[aria-pressed="true"] { background: var(--ink); color: var(--paper); }

/* Empty until the live totals arrive (app.js). The min-width reserves room for
   a two-digit count so the pill does not visibly resize when they land, and an
   unreachable API just leaves the button reading «проголосовать». */
.vote__count { font: 400 10.5px/1 var(--mono); min-width: 2ch; text-align: right; }
.vote__count:empty { min-width: 0; }

/* The check is drawn, not typed: neither Montserrat nor a system Cyrillic
   fallback is guaranteed to carry U+2713, and a tofu box in the voted state
   would be the one glyph every voter sees. */
.vote__check { display: none; width: 11px; height: 11px; }
.vote[aria-pressed="true"] .vote__check { display: block; }
.vote[aria-pressed="true"] .vote__label { display: none; }

/* ── Feedback form ────────────────────────────────────────────────────── */

.feedback { padding: 30px clamp(20px, 5vw, 60px) 110px; }

.feedback__title {
  margin-bottom: 26px;
  font: 400 clamp(28px, 7vw, 44px)/1.05 var(--serif);
  letter-spacing: -0.02em;
}

.feedback__form { max-width: 720px; display: flex; flex-direction: column; gap: 18px; }

.feedback__label { display: flex; flex-direction: column; gap: 7px; }

/* Weight matches the design bundle. The line-height deliberately does not:
   the original set 1, which fits its single desktop line and crowds the two
   or three this label wraps to on a phone. */
.feedback__label > span { font: 500 13.5px/1.3 var(--sans); color: var(--muted); }

.feedback__input {
  width: 100%;
  height: 110px;
  resize: vertical;
  padding: 12px 14px;
  border: 1px solid var(--field-border);
  border-radius: 3px;
  background: var(--field);
  color: var(--ink);
  font: 400 14.5px/1.6 var(--sans);
  outline: none;
}

.feedback__input:focus { border-color: var(--ink); }

.feedback__actions {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  padding-top: 20px;
}

.feedback__submit {
  padding: 12px 28px;
  border: 1px solid var(--ink);
  border-radius: 3px;
  background: var(--ink);
  color: var(--paper);
  cursor: pointer;
  font: 500 12px var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background 0.2s, border-color 0.2s;
}

.feedback__submit:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); }
.feedback__submit:disabled { opacity: 0.55; cursor: default; }

.feedback__status { font: 400 11px/1.5 var(--mono); color: var(--muted); }
.feedback__status[data-state="error"] { color: #a34f3c; }

/* ── Focus ────────────────────────────────────────────────────────────── */

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

/* ── The scatter ──────────────────────────────────────────────────────────
   Wide viewports only, and only once app.js has put `js` on <html> and is
   maintaining --k. Below this width the composition is abandoned rather than
   scaled further: a 1120px canvas shrunk onto a phone renders every card
   unreadably small. */
@media (min-width: 760px) {
  .js .scatter-frame {
    /* The frame reserves the scaled height. A transform does not affect layout,
       so without this the page would collapse under the canvas. */
    height: calc(1920px * var(--k));
    overflow: hidden;
  }

  .js .scatter {
    width: 1120px;
    height: 1920px;
    transform: scale(var(--k));
    transform-origin: top left;
  }

  .js .thread { display: block; }

  .js .card {
    position: absolute;
    left: var(--x);
    top: var(--y);
    width: var(--w);
    max-width: none;
    margin: 0;
  }

  .js .card__media { height: var(--h); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ══ Card widgets ═══════════════════════════════════════════════════════
   Two cards illustrate a feature that no photograph shows well, so they carry
   a small built-from-divs animation instead of an image. Both are decorative:
   the card's title is what names the feature. */

/* ── 01 · Sticker sheet that runs out ─────────────────────────────────── */

/* Stacked sticker layers peel off one by one, exposing the dashed outline of
   the sheet beneath, then the stack returns — the loop makes the point that a
   sheet is finite without any copy. */
@keyframes sticker-peel {
  0%, 12%   { transform: none; opacity: 1; }
  15%       { transform: rotate(-6deg) translate(2px, -7px) scale(1.05); opacity: 1; }
  20%       { transform: rotate(-17deg) translate(16px, -28px) scale(0.88); opacity: 0; }
  20.01%, 90% { transform: rotate(-17deg) translate(16px, -28px) scale(0.88); opacity: 0; }
  95%       { transform: rotate(2deg) scale(1.03); opacity: 1; }
  100%      { transform: none; opacity: 1; }
}

@keyframes sticker-ghost {
  0%, 1%    { opacity: 0; }
  5%, 88%   { opacity: 1; }
  94%, 100% { opacity: 0; }
}

/* Height comes from .card__media, which both widgets also carry — so they
   track the viewport on one column exactly as the photo cards do instead of
   staying frozen at their desktop height. */
.stickers {
  position: relative;
  overflow: hidden;
  background: #f5efe4 radial-gradient(rgba(58, 65, 72, 0.13) 1px, transparent 1px);
  background-size: 14px 14px;
}

.stickers__grid {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 16px;
  padding: 20px;
}

.sticker { position: relative; }

.sticker__ghost,
.sticker__layer {
  position: absolute;
  inset: 0;
  border-radius: var(--shape);
}

.sticker__ghost {
  border: 1.4px dashed rgba(58, 65, 72, 0.32);
  animation: sticker-ghost 14s linear infinite;
  animation-delay: var(--d);
}

.sticker__layer {
  background: var(--c);
  /* Each layer sits a little down-left of the one above, so the stack reads as
     a stack rather than one shape. */
  margin: var(--o) calc(-1 * var(--o)) calc(-1 * var(--o)) var(--o);
  animation: sticker-peel 14s cubic-bezier(0.3, 0.7, 0.3, 1) infinite;
  animation-delay: var(--d);
}

.sticker__layer--top { box-shadow: 0 3px 6px rgba(58, 65, 72, 0.28); }

/* ── 03 · App themes ──────────────────────────────────────────────────── */

.themes {
  padding: 14px;
  background: #f5efe4;
  display: flex;
  flex-direction: column;
  gap: 10px;

  /* The live theme, repainted by app.js when a swatch is picked. */
  --bg: #fbf6ec;
  --block: #e9ddc9;
  --line: #efe6d6;
  --theme-accent: #bd7566;
  --wind: none;
  --leaf: none;
}

.themes__page {
  position: relative;
  flex: 1;
  border-radius: 10px;
  background: var(--bg);
  overflow: hidden;
  box-shadow: 0 6px 14px -10px rgba(50, 42, 36, 0.55);
}

/* A miniature diary page: a photo block, a heading rule and three text rules. */
.themes__photo { position: absolute; left: 14px; top: 14px; right: 14px; height: 46%; border-radius: 7px; background: var(--block); }
.themes__head  { position: absolute; left: 14px; top: calc(46% + 24px); width: 44%; height: 8px; border-radius: 4px; background: var(--theme-accent); }
.themes__line  { position: absolute; left: 14px; height: 6px; border-radius: 3px; background: var(--line); }
.themes__line:nth-of-type(1) { top: calc(46% + 42px); width: 72%; }
.themes__line:nth-of-type(2) { top: calc(46% + 56px); width: 62%; }
.themes__line:nth-of-type(3) { top: calc(46% + 70px); width: 38%; }

/* Per-theme ornaments: only the theme that sets its display to `block` shows
   them, so one rule serves all four. */
.themes__wind { display: var(--wind); position: absolute; border-top: 3px solid; border-radius: 50%; }
.themes__wind:nth-of-type(1) { right: 16px; top: 20px; width: 62px; height: 10px; border-color: rgba(255,255,255,0.75); transform: rotate(-8deg); }
.themes__wind:nth-of-type(2) { right: 10px; top: 38px; width: 46px; height: 9px;  border-color: rgba(255,255,255,0.6);  transform: rotate(6deg); }
.themes__wind:nth-of-type(3) { right: 24px; top: 56px; width: 34px; height: 8px;  border-color: rgba(255,255,255,0.5);  transform: rotate(-4deg); }

.themes__leaf { display: var(--leaf); position: absolute; border-radius: 50% 0; }
.themes__leaf:nth-of-type(1) { right: 18px; top: 18px;    width: 14px; height: 22px; background: #6a9150; transform: rotate(22deg); }
.themes__leaf:nth-of-type(2) { right: 38px; top: 44px;    width: 12px; height: 19px; background: #86a86a; transform: rotate(-30deg); }
.themes__leaf:nth-of-type(3) { right: 14px; bottom: 20px; width: 11px; height: 17px; background: #6a9150; transform: rotate(48deg); }

.themes__swatches { display: flex; justify-content: center; gap: 14px; }

.themes__swatch {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid rgba(74, 66, 60, 0.35);
  border-radius: 50%;
  background: var(--c);
  cursor: pointer;
}

.themes__swatch[aria-pressed="true"] { box-shadow: 0 0 0 2.5px #4a423c; }
