/* Canvas layout — generated positions live in the markup, this is the frame.
   See design/gen_canvas.py and design/register-desktop.tsv. */

/* Fonts. The generated markup names families exactly as Figma reports them
   (Involve, Postertoaster, Unbounded), so these declarations must match those
   names or every text node silently falls back and re-wraps. */
@font-face {
  font-family: "Involve";
  src: url("/assets/fonts/Involve-SemiBoldOblique.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Involve";
  src: url("/assets/fonts/Involve-BoldOblique.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Postertoaster";
  src: url("/assets/fonts/Postertoaster.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Unbounded sets the FAMILY / OFFICE / EDUCATION logotype. It is in the design
   but was absent from the repo, which is why that mark rendered as a plain
   fallback — the designer's "боковой логотип ... совсем должен быть другим".
   SIL Open Font License, see assets/fonts/Unbounded-OFL.txt. */
@font-face {
  font-family: "Unbounded";
  src: url("/assets/fonts/Unbounded-Regular-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  font-family: "Unbounded";
  src: url("/assets/fonts/Unbounded-Regular-cyrillic.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* One design pixel == 1rem. The artboard is 1440 wide, so the root font-size
   is 1/1440th of the viewport and every generated length scales with it, with
   no JavaScript and no breakpoint ladder. Capped at 1px so the canvas stops
   growing past its design size and centres instead.
   Text sizes are all emitted explicitly, so nothing depends on the browser's
   default font size — but this does mean the page ignores a user's default
   font-size preference, which is the cost of a pixel-exact artboard. */
html {
  font-size: min(calc(100vw / 1440), 1px);
}

html, body {
  margin: 0;
  padding: 0;
  background: transparent;
}

/* The artboard frame clips its content, and so does this.
   --------------------------------------------------
   The design draws past the frame edge in several places — the hero
   composition is 1531 wide on a 1440 artboard, 588 on a 390 one — and Figma
   simply cuts it off at the frame. Here that overflow is real, so without a
   clip the page can be dragged sideways to a wide margin of stray artwork.

   On `html`, not just `body`. A `body` clip is supposed to propagate to the
   viewport when the root's overflow is visible, and on desktop it did, which
   is why this went unnoticed — but on a phone the page still panned right by
   several hundred pixels.

   `clip` rather than `hidden`: hidden makes the box a scroll container, which
   forces the other axis to `auto` and leaves the page programmatically
   scrollable sideways; clip only clips. `hidden` stays first as the fallback
   for anything that does not know `clip`.

   Only the horizontal axis. Clipping vertically would cut the page off at the
   viewport. */
html,
body {
  overflow-x: hidden;
  overflow-x: clip;
}

.canvas {
  position: relative;
  margin: 0 auto;
  background: transparent;
  /* Deliberately NOT overflow:hidden. The Figma frame clips its content, and
     at viewports up to 1440 the body's overflow-x does exactly that job since
     the canvas is then the full viewport width. Clipping here as well would
     also clip the full-bleed bands back to the artboard, which is the whole
     thing this is meant to fix. */

  /* Vertically, though, the artboard does clip, as the Figma frame does.
     `clip` rather than `hidden` so the horizontal axis can stay visible (hidden
     would turn it into a scroll container on both axes).

     What it cuts off is the bottom of the last torn sheet. Vector art is
     exported with a one-pixel transparent margin (build_svgs.py pads every
     asset so an edge is never shaved by the <img> viewport), and the last
     sheet ends exactly where the artboard does — so its margin hung a pixel
     below the page, made the document a pixel taller than the design, and let
     the cream body show through as a pale line under the black footer. */
  overflow-y: clip;
}

/* The seam under the footer.
   Clipping removes the stray margin, but at most widths the artboard's height
   is not a whole number of pixels — 14385 design px at 1280/1440 is 12786.67 —
   so its last device row is part footer, part cream, and blends to grey. This
   strip paints the bottom two pixels in the footer's own #171717 across the
   whole window, over that blended row. It sits inside the artboard, so it adds
   nothing to the page's height, and 100vw rather than the artboard's width
   because above 1440 the footer band is stretched to the window either side.
   Not `body` in #171717: above 1440 the body is what shows either side of the
   artboard, and it must stay cream. */
.canvas::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 100vw;
  height: 2px;
  transform: translateX(-50%);
  background: #171717;
  pointer-events: none;
}

/* Both artboards carry their own width and height inline, from the design.
   The mobile one is hidden by default and the media query below turns that
   round: it has to come after this rule, or a rule of equal weight later in
   the file wins and the mobile artboard stays hidden at every width. */
.canvas--mobile { display: none; }

/* The designer drew two artboards, 1440 and 390 wide, with different content
   in different places — not one layout that reflows. So both are in the
   document and this chooses between them. There is no third layout to show at
   tablet widths, so the switch is a single line rather than a ladder.

   900px is the crossover. Below it the desktop artboard would be scaled to
   0.62 and its 23px body copy would read at 14px; above it the mobile artboard
   would have to be blown up past 2.3x.

   The mobile artboard is capped at 1.5 the same way the desktop one is capped
   at 1: past 585px wide it stops growing and centres, so a phone layout in a
   900px window reads at its intended size instead of being magnified. Its 11px
   body copy tops out at 16.5px there. */
@media (max-width: 900px) {
  html {
    font-size: min(calc(100vw / 390), 1.5px);
  }
  .canvas--desktop { display: none; }
  .canvas--mobile  { display: block; }
}

/* Every generated element. Position comes from the node's own transform, so
   left/top stay at zero and transform-origin must be the top-left corner to
   match Figma's coordinate space. */
.fg {
  position: absolute;
  left: 0;
  top: 0;
  margin: 0;
  transform-origin: 0 0;
  box-sizing: border-box;
}

img.fg {
  display: block;
  object-fit: contain;
}

/* Text boxes are flex columns so Figma's textAlignVertical maps onto
   justify-content. pre-wrap keeps the designer's explicit line breaks. */
.ft {
  display: flex;
  flex-direction: column;
  white-space: pre-wrap;
  font-kerning: normal;
}

/* Progressive blur — the hero's «FAMILY OFFICE».
   ----------------------------------------------
   The lettering is sharp beside the children and blurs out towards both edges
   of the page, as the design draws it; that blur is also what hides the joins
   in the letter outlines (the jog in the F's stem where its bar meets it).
   CSS has no blur that varies across an element, so design/gen_canvas.py
   (progressive_art, and the note above PB_LEVELS) writes each blurred group as
   five copies of its art at rising `filter: blur()`, each masked to the stretch
   of the ramp where its radius applies. The masks add up to exactly one
   everywhere, and `plus-lighter` sums the copies rather than stacking them, so
   every point is a clean mix of the two blur levels either side of it — a
   blurry copy half-transparent at a letter's edge cannot let the sharp copy's
   edge show through. `isolation` confines that sum to one group's copies; it
   must not reach the cream behind, or «FAMILY»'s copies into «OFFICE»'s.

   The copies are padded (inline, per group) so the blur has room to spread:
   a mask clips at the element's own box, and without the padding the blurred
   letters stopped in a hard line at the top of the art's box. */
.fg-pblur {
  position: absolute;
  inset: 0;
  isolation: isolate;
  pointer-events: none;
}
.fg-pblur-l {
  position: absolute;
  display: block;
  box-sizing: content-box;
  max-width: none;
  -webkit-mask-image: var(--pb-mask);
  mask-image: var(--pb-mask);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.fg-pblur-add { mix-blend-mode: plus-lighter; }

/* Figma's Glass effect.
   ----------------------
   Glass has six real parameters in Figma (lightIntensity, lightAngle,
   refraction, depth, dispersion, radius) but GLASS is not in the REST API's
   typed effect union at all, so the file only ever tells us {type, visible}.
   The values below are therefore calibrated against sampled pixels from a
   reference render of the design, not read from it.

   What IS reproduced by mechanism rather than by guesswork: `radius` is a
   background blur, so a genuine backdrop-filter makes the small orange
   rectangle behind each asterisk blur outward into the centre glow, and makes
   an overlapping pane bleed a soft band across its neighbour's edge. Those two
   effects previously had to be hand-drawn as gradients and could not adapt to
   what was actually behind them.

   Note mask-image rather than clip-path: masking is applied after filters, so
   clip-path does not confine a backdrop-filter to a non-rectangular shape. */
.fg-glass {
  --frost: 26rem;
  -webkit-mask-image: var(--mask, none);
  mask-image: var(--mask, none);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  backdrop-filter: blur(var(--frost)) saturate(1.1);
  -webkit-backdrop-filter: blur(var(--frost)) saturate(1.1);
}

/* The tint: the node's own fill at its own opacity, composited normally.
   Measured against the reference, a pane over the #171717 ground reads 76,
   which is exactly #fffcec at 0.23 blended normally (23 + (252-23)*0.23).
   An earlier attempt used mix-blend-mode: multiply here — that cannot lighten,
   so it could not produce that value at all. */
.fg-glass::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--tint, transparent);
  opacity: var(--tint-a, 0.23);
}

/* The specular rim (Figma's lightIntensity / lightAngle).

   Masked with a stroked outline of the shape rather than an inset box-shadow:
   box-shadow draws the element's rectangle, which the shape mask then cuts
   away, so an asterisk got no visible edge at all. */
.fg-glass::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 246, 236, 0.55);
  -webkit-mask-image: var(--rim, none);
  mask-image: var(--rim, none);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

a.fg { text-decoration: none; color: inherit; }

.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 999;
}
.skip-link:focus {
  left: 8rem;
  top: 8rem;
  padding: 8rem 12rem;
  background: #fffcec;
  color: #000;
  font: 16rem/1.2 system-ui, sans-serif;
}

/* Full-bleed background bands.

   Above 1440 the artboard holds 1:1 and centres, so without this the dark
   sections stop at the artboard edge with page background either side and the
   page reads as letterboxed.

   Scoped to viewports wider than the artboard on purpose: at and below its
   design width the bands keep their exact design geometry, so the geometry
   check — which measures at 1440 and at 390 — still compares against the real
   design box. Only once the artboard has stopped growing and started to centre
   does the rule relax. That is past 1440 for the desktop artboard and past 585
   for the mobile one, which is where its 1.5 scale cap bites. */
@media (min-width: 1441px), (min-width: 586px) and (max-width: 900px) {
  .fg-bleed {
    left: 50%;
    /* overscan: 100vw counts the scrollbar gutter while the layout width
       does not, so an exact 100vw band lands a few px short on one side */
    width: calc(100vw + 60rem) !important;
    transform: translate(-50%, var(--bleed-y)) !important;
    /* stretch rather than letterbox: object-fit:contain would preserve the
       torn sheet's aspect ratio and leave transparent bars either side */
    object-fit: fill !important;
  }
}

/* ---- Interactive elements ------------------------------------------------
   The design draws links, fields and the accordion as plain rectangles and
   text. design/semantics.tsv says which node is which; these rules give them
   the behaviour, keeping the design's own geometry. */

a.fg:focus-visible,
.fg-field:focus-visible,
.fg-submit:focus-visible,
.fg-faq-head:focus-visible {
  outline: 3rem solid #ff6a34;
  outline-offset: 2rem;
}

a.fg { cursor: pointer; }

/* Link hover states — the designer's own.
   ---------------------------------------
   Every button in the design is a component drawn twice, Default and
   Variant2, with a prototype interaction between the two: on MOUSE_ENTER
   change to Variant2, on MOUSE_LEAVE back, ease-out, 0.3s. Variant2 is the
   hover state, so none of it is invented here:
     - the orange buttons empty to a 3px orange outline on the dark ground,
       text unchanged (they used to go "просто чуть светлее" under a generic
       brightness filter, which is what the designer flagged);
     - the header's glass buttons swing their orange inner shadow from the
       top-left corner to the bottom-right;
     - the contacts — phone, mail, Instagram, Telegram — go from beige to
       orange, the whole of each, where the filter used to turn them white.
   The footer address has no component of its own and borrows the contacts'
   change, so the row answers the pointer alike. The mobile artboard has no
   components at all and takes its desktop counterpart's, scaled to its own
   drawing.

   design/gen_canvas.py reads the two variants and marks up what differs:
   `fg-hv` on the link that is hovered, and on whichever part changes a class
   saying what changes (fg-hv-bg, -shadow, -ink, -fill) with the value to
   change to in a custom property of the same name. So these rules carry no
   colour of any button — recolour one in Figma and the hover follows.

   !important is load-bearing, as in the ecosystem cards below: the resting
   colours are inline styles, which outrank any selector. The one exception
   is `fill`, set on the shapes of inline art by presentation attributes,
   which any rule outranks.

   Timing comes from the design as well (--hv-dur, --hv-ease), and is the
   same both ways — the MOUSE_LEAVE transition is the MOUSE_ENTER one. */
.fg-hv-bg,
.fg-hv-shadow,
.fg-hv-ink,
.fg-hv-fill path {
  transition-property: background-color, box-shadow, color, fill;
  transition-duration: var(--hv-dur, .3s);
  transition-timing-function: var(--hv-ease, ease-out);
}

/* The part that changes is either the link itself (a desktop button carries
   its own fill) or something inside it (a mobile button's fill is on a
   rectangle in its group; a phone number's colour is on its text) — hence
   each rule twice. Gated on a real pointer, like the cards below: a touch
   browser holds :hover after a tap. */
@media (hover: hover) {
  .fg-hv:hover.fg-hv-bg,
  .fg-hv:hover .fg-hv-bg { background-color: var(--hv-bg) !important; }
  .fg-hv:hover.fg-hv-shadow,
  .fg-hv:hover .fg-hv-shadow { box-shadow: var(--hv-shadow) !important; }
  .fg-hv:hover.fg-hv-ink,
  .fg-hv:hover .fg-hv-ink { color: var(--hv-ink) !important; }
  .fg-hv:hover .fg-hv-fill path { fill: var(--hv-fill); }
}

/* A link reached by keyboard takes the same state as one under the pointer,
   on top of its focus ring: the state is the design's answer to "this is the
   one you are on", and a keyboard user is on it as much as a mouse user.
   Not gated on hover — a keyboard on a touch device still has focus. */
.fg-hv:focus-visible.fg-hv-bg,
.fg-hv:focus-visible .fg-hv-bg { background-color: var(--hv-bg) !important; }
.fg-hv:focus-visible.fg-hv-shadow,
.fg-hv:focus-visible .fg-hv-shadow { box-shadow: var(--hv-shadow) !important; }
.fg-hv:focus-visible.fg-hv-ink,
.fg-hv:focus-visible .fg-hv-ink { color: var(--hv-ink) !important; }
.fg-hv:focus-visible .fg-hv-fill path { fill: var(--hv-fill); }

/* Touch targets.
   -------------
   The design sizes its links to be read, not pressed: the mobile header's are
   15px tall against the ~44px a thumb wants. Rather than move or resize
   anything the designer drew, each link carries the room it may take in
   --tap-x / --tap-y and spends it on a transparent pseudo-element. The link's
   own box, and so everything the geometry check measures, is untouched.

   The values are computed per link and capped at half the distance to the next
   interactive thing, so no two targets can claim the same pixel — see
   _tap_targets in design/gen_canvas.py. Links that need nothing get an inset
   of zero, which is exactly their own box. */
a.fg::after {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--tap-y, 0rem)) calc(-1 * var(--tap-x, 0rem));
}

/* The consent tick and its sentence are one label, so the sentence toggles it.
   The tick alone is 17px square on the mobile artboard. */
label.fg { cursor: pointer; }

/* Ecosystem cards — the hover swap.
   ---------------------------------
   The designer's animation note in the Figma file (node 9:1561) asks that in
   «Не просто учеба. Экосистема развития» each rectangle "становился
   противоположным" while the pointer is over it: an outlined card with black
   text fills orange and takes a yellow heading over beige body copy, and a
   filled card empties back to an orange outline with black text. The file
   draws both states — every card is a component with a Default and a Variant2
   — and the colours below are read from those variants, not chosen here.

   Hover only, with no timed sequence: the prototype recording looks like the
   cells lighting up in turn, but the wash is following the presenter's cursor.
   Animated unprompted, the grid blinks at a reader who is pointing at nothing,
   which reads as a fault rather than as feedback.

   The two artboards build a card differently, and the rules below cover both.
   Desktop: one node carrying the fill, with the two text nodes inside it.
   Mobile: a group holding a rectangle that carries the fill, then a frame of
   text. So the card's own background is what fills, and a filled child is what
   gets cleared — on desktop that child selector matches nothing, on mobile it
   is the rectangle. --swap-w is the artboard's own stroke weight, 5px on the
   desktop cards and 2.2 on the mobile ones; it is also the designer's "линия
   ... неодинаковые" note answered, one weight for all six.

   !important is load-bearing, not habit. Every colour in this port is an
   inline style emitted from the design, and an inline style outranks any
   selector — so a state that changes one has no other way to win.

   Gated on a real pointer. The note says "при наведении мышки", and a touch
   browser resolves :hover on tap and then holds it until the reader taps
   something else — on a card that does nothing when pressed, a highlight that
   stays behind reads as a stuck screen rather than as an answer to a tap.

   0.3s ease-out, both ways: the interaction the file draws between each
   card's Default and Variant2 (the sets in «Блок Экосистема развития»), and
   the same timing as every button's. */
@media (hover: hover) {
  .fg-swap,
  .fg-swap > div,
  .fg-swap .ft {
    transition: background-color .3s ease-out, box-shadow .3s ease-out, color .3s ease-out;
  }

  /* outlined -> filled. The heading and the body take different colours, and
     position is what tells them apart: the desktop artboard's heading is an h3
     and its body a p, but the mobile artboard sets both in 19px/11px Involve,
     so both come out as p. In each the heading leads its box and the body
     follows, whether that box is the card itself or the frame inside it. */
  .fg-swap--line:hover { background: #ff6a34 !important; }
  .fg-swap--line:hover .ft:first-child  { color: #faeea8 !important; }
  .fg-swap--line:hover .ft:nth-child(2) { color: #fffcec !important; }

  /* filled -> outlined */
  .fg-swap--fill:hover {
    background: transparent !important;
    box-shadow: inset 0 0 0 var(--swap-w, 5rem) #ff6a34 !important;
  }
  .fg-swap--fill:hover > div { background: transparent !important; }
  .fg-swap--fill:hover .ft { color: #171717 !important; }
}

/* Format cards — come forward under the pointer.
   ----------------------------------------------
   The designer's note on «Выберите формат сопровождения»: «при наведении на
   каждый вариант сделать, чтобы он выходил на первый план, потом при отведении
   мышки с него возвращался на свое место». On the desktop artboard the three
   glass cards overlap, START over STRATEGY over FULL SUPPORT, so the hovered
   one is raised over its neighbours (--lift-z, one above the highest of the
   three, emitted by design/gen_canvas.py) and lifts a little towards the
   reader. The file draws no state for this, so the size of the lift is set
   here: a rise of 2.5% of the card's own height and 3% larger, which is a
   13px rise on a desktop card and 2px on the much flatter mobile ones — a
   percentage so each card lifts in proportion to itself.

   Why --lift and not the `scale` / `translate` properties: those compose
   OUTSIDE `transform`, and every element here carries its position in
   `transform` with its origin at the top-left corner, so a `scale` there
   scales the card's distance from the artboard's corner too and throws it
   hundreds of pixels across the page. The generator leaves a slot at the end
   of the card's own transform — `translate(x, y) var(--lift, translate(0))`
   — so the lift is applied after the card is in place, about its own centre.
   Registered, so it animates rather than jumps; without @property support it
   still takes effect, just without the easing.

   On leave the card keeps its raised z-index until it has settled (the 0s
   step delayed by the 0.3s the lift takes), or it would drop behind its
   neighbour while still enlarged and be cut by it on the way down.

   Its glass keeps working while raised: the backdrop blur is on a child of
   the card, and a transform on the card does not stop it seeing what is
   behind. */
@property --lift {
  syntax: "<transform-list>";
  inherits: false;
  initial-value: translateY(0px) scale(1);
}
.fg.fg-lift {
  transform-origin: 50% 50%;
  transition: --lift .3s ease-out, z-index 0s linear .3s;
}
@media (hover: hover) {
  .fg-lift:hover {
    --lift: translateY(-2.5%) scale(1.03);
    z-index: var(--lift-z) !important;
    transition: --lift .3s ease-out, z-index 0s;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fg-swap,
  .fg-swap > div,
  .fg-swap .ft,
  .fg-hv-bg,
  .fg-hv-shadow,
  .fg-hv-ink,
  .fg-hv-fill path,
  .fg.fg-lift,
  .fg-lift:hover { transition: none; }
}

/* The «формат» running line.
   --------------------------
   The designer's note: «Здесь бегущая строка, беспрерывная в любую сторону».
   Her prototype only slides the line 300px left over three seconds and back;
   the owner chose the continuous line the note describes, running left.

   The line's content sits in .fg-ticker-run, a wrapper the generator adds at
   the line's own 0,0, and it is the wrapper that moves. Not the parts
   themselves: each carries its position in its own `transform`, and an
   animation of `transform` there would replace it and throw the words to the
   artboard's corner.

   One loop is exactly one repeat — «формат», its gap and a dot — so the last
   frame and the first are the same picture and the restart cannot be seen.
   That length, --ticker-period, is read from the design by the generator from
   the spacing of the dots (393.105 on the desktop artboard), and the words
   are held to the same spacing by boxing each one to that width
   (.fg-ticker-slot): left to the font, a word and its five spaces come to
   391.72, and the dots would slip a pixel and a half against them on every
   loop. The design already runs the words and dots well past the band's
   right edge, further than one repeat, so the band is never short of
   something to show and needs no second copy.

   Speed is ours, in design pixels per second — the prototype's slide averages
   100. At 80 a word takes about twenty seconds to cross the band, and one
   loop is 393.105 / 80 = 4.9 seconds. It is in rem
   like everything else, so a narrower window scales the distance and the
   speed together and the line looks the same at every width. Linear, since
   an easing would pulse at every restart.

   Still under reduced motion: a line that never stops moving is exactly what
   that setting asks a page not to do. */
.fg-ticker { --ticker-speed: 80; }
.fg-ticker-run {
  position: absolute;
  inset: 0;
  animation: fg-ticker calc(var(--ticker-period) * 1s / var(--ticker-speed)) linear infinite;
}
.fg-ticker--right .fg-ticker-run { animation-direction: reverse; }
@keyframes fg-ticker {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--ticker-period) * -1rem)); }
}
.fg-ticker-slot { display: inline-block; }
@media (prefers-reduced-motion: reduce) {
  .fg-ticker-run { animation: none; }
}

/* Form controls sit exactly on the rectangles the designer drew.
   Size and padding are emitted per artboard, since the two are set at
   different scales — see FIELD_TEXT in design/gen_canvas.py. */
.fg-field,
.fg-submit {
  font-family: "Involve", sans-serif;
  font-weight: 600;
  color: #171717;
  appearance: none;
}
textarea.fg-field {
  resize: none;
}
input.fg-field[type="checkbox"] {
  padding: 0;
  cursor: pointer;
}

/* A ticked box has to look ticked. `appearance: none` above is what lets the
   box keep the exact size, fill and border the design draws for it, and it
   also throws away the browser's own tick — leaving a required control that
   looks identical whether or not it has been agreed to. The mark is drawn
   here instead: dark, so it reads on the cream box on either artboard, and as
   a background image because ::after does not render on an input. */
input.fg-field[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.2 8.6l3.1 3.4L12.8 4.6' fill='none' stroke='%23171717' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 72% 72%;
}
.fg-submit {
  cursor: pointer;
  text-align: center;
  color: #171717;
}
.fg-submit:hover { filter: brightness(1.06); }

/* FAQ accordion. <details name="faq-…"> gives exclusive open/close natively.
   The answer hangs below its question, out of the canvas's flow — nothing on
   this page is in flow — so js/faq.js moves what follows it out of the way.
   Type size, line height, alignment and padding are emitted per artboard: on
   the desktop one they are the design's own answer blocks, 23/30.59 justified
   across the full 1256 of the row; on the mobile one they match that
   artboard's body copy, justified the same way. */
.fg-faq {
  overflow: visible;
}
.fg-faq-head {
  display: block;
  position: absolute;
  inset: 0;
  cursor: pointer;
  list-style: none;
}
.fg-faq-head::-webkit-details-marker { display: none; }

.fg-faq-body {
  position: absolute;
  left: 0;
  top: 100%;
  width: 100%;
  z-index: 5;
  box-sizing: border-box;
  background: #fffcec;
  font-family: "Involve", sans-serif;
  font-weight: 600;
  color: #171717;
}

/* Room for an open answer.
   ------------------------
   The designer's note on this block is explicit: "главное, чтобы ответ на
   текущий вопрос не закрывал следующий вопрос". The gaps the design leaves
   between rows hold six of the eight desktop answers and none of the mobile
   ones, so the rest of the page has to give way.

   Everything on the canvas is placed at a fixed coordinate and nothing is in
   flow, so an opening answer moves nothing by itself. js/faq.js measures the
   overrun and puts it in --faq-push; these two rules spend it. `translate` is
   the individual transform property, which composes with the `transform` each
   element already carries rather than replacing it — the elements keep their
   design positions and are shifted from there. The canvas grows by the same
   amount so the page below the fold stays reachable.

   With no JavaScript --faq-push stays 0: the accordion still opens, and a long
   answer overlaps the next question the way it did before.

   Two selectors because the two artboards nest the accordion differently: the
   desktop one has the eight rows as siblings of everything else on the page,
   the mobile one wraps them in a group. The first rule moves the rows after
   the open one; the second moves whatever follows the group that holds it, at
   whatever depth that group sits — without it the push stops at the group's
   edge and the answer pushes the remaining questions straight into the section
   below. */
.fg-faq[open] ~ .fg,
.fg:has(.fg-faq[open]) ~ .fg {
  translate: 0 var(--faq-push, 0rem);
}
.canvas {
  padding-bottom: var(--faq-push, 0rem);
}

/* An open item must sit above the questions below it, or its answer is drawn
   underneath them: each FAQ node carries its own design z-index and later
   siblings are higher. */
.fg-faq[open] {
  z-index: 90 !important;
}

/* The plus turns into a cross while its answer is open.
   ------------------------------------------------------
   The designer's note: «Плюсик, в свою очередь, меняется на крестик», and her
   prototype shows the × in the ring while an answer is open. A quarter of a
   right angle does it: the plus turned 45° is the cross.

   Only the two bars turn, never the ring. The icon is written inline (see
   _faq_marks in design/gen_canvas.py) with each bar in a group of its own,
   and it is those groups that rotate — each about the centre of its own
   drawn box (`transform-box: fill-box`), which for both bars is the point
   where they cross. So the ring, and the icon's box, do not move by so much
   as a pixel.

   The first attempt rotated the whole icon with the `rotate` property and
   sent it ~800px down the page. `rotate` composes OUTSIDE `transform`, and
   this element's position lives in `transform: translate(...)` with its
   origin at the top-left corner — so the rotation swung the icon's whole
   offset from the corner of its row, not the icon about its middle. Turning
   the whole icon about its centre would not have been right either: the ring
   is not quite round (the second desktop one is 83.4 by 85), so it would
   visibly change shape as it turned. */
.fg-faq-bar {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform .3s ease-out;
}
.fg-faq[open] .fg-faq-bar {
  transform: rotate(45deg);
}
@media (prefers-reduced-motion: reduce) {
  .fg-faq-bar { transition: none; }
}
