/* ==========================================================================
   Dino mokyklėlė — public site
   Direction: "Drawn, not rendered." See DESIGN.md.

   Plain CSS on purpose: the production host is shared cPanel with no node,
   so this file is uploaded and served exactly as written. No build step,
   no preprocessor, no CDN — every font byte is local under /public/fonts.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts — self-hosted, latin + latin-ext.

   Both faces are variable, so one file per subset covers every weight we use,
   and Lithuanian (ąčęėįšųūž) lives in the latin-ext subset. font-weight is
   declared as a RANGE: naming a single weight on a variable file makes the
   browser synthesise the others, which is what turns 800 into faux-bold mush.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url(../fonts/baloo2-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url(../fonts/baloo2-latinext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url(../fonts/figtree-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url(../fonts/figtree-latinext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   2. Tokens

   Seven page colours. The candy accents below them are capped by rule: they
   may fill an icon chip or a badge, never a section, never anything over
   48px. That cap is what stops 66 games turning into 66 colours.
   -------------------------------------------------------------------------- */
:root {
  --paper: #FDF6EC;   /* warm off-white — the page ground */
  --sand:  #F5E6CE;   /* deeper warm band — alternating sections */
  --ink:   #274156;   /* ALL text, ALL outlines, ALL edges. Never black. */
  --leaf:  #51CF66;   /* brand green — Dinas, primary CTA */
  --sky:   #7ECBFF;   /* the app's own sky — the final section only */
  --coral: #FF6B6B;   /* the one loud accent */
  --sun:   #FFD43B;   /* stars, badges, the headline swash */

  /* candy accents — 44px icon chips only, never a background */
  --c-orange: #FFA94D;
  --c-teal:   #38D9A9;
  --c-blue:   #4DABF7;
  --c-purple: #B197FC;
  --c-pink:   #F783AC;

  /* Ink at reduced strength for secondary text. 80%, not the 72% this was
     first drawn at: measured against the SAND ground 72% gives 4.21:1, which
     fails AA for body text. 80% measures 5.1:1 on sand and 5.4:1 on paper, so
     captions pass on both grounds rather than only on the lighter one. */
  --ink-soft: rgba(39, 65, 86, .80);
  --ink-edge: rgba(39, 65, 86, .16);

  /* four radii, four jobs. A single global radius is the generator tell. */
  --r-chip:  12px;   /* chips, badges, eyebrow pills, inputs */
  --r-pill:  999px;  /* buttons, store buttons, language switcher */
  --r-card:  24px;   /* feature, parent and FAQ cards */
  --r-frame: 30px;   /* screenshot frames, hero frame */

  /* the app's own release curve — bouncy, physical */
  --spring: cubic-bezier(.34, 1.56, .64, 1);

  /* three-layer depth, no gradients: solid candy edge + soft ambient */
  --edge:    0 6px 0 var(--ink-edge), 0 12px 22px rgba(39, 65, 86, .10);
  --edge-sm: 0 4px 0 var(--ink-edge), 0 8px 14px rgba(39, 65, 86, .08);
  --edge-press: 0 2px 0 var(--ink-edge), 0 4px 8px rgba(39, 65, 86, .08);

  --font-display: 'Baloo 2', 'Arial Rounded MT Bold', 'Trebuchet MS', sans-serif;
  --font-body: 'Figtree', 'Helvetica Neue', Arial, sans-serif;
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

/* Paper grain. An SVG turbulence tile as a data URI — irregular by
   construction, ~400 bytes, no asset pipeline. At 3.5% it is the difference
   between "warm paper" and "beige rectangle". Sits above everything and
   ignores the mouse so it can never eat a click. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

img, svg { max-width: 100%; }
img { height: auto; display: block; }
a { color: inherit; }

/* Anchor targets stop short of the viewport edge, so a jumped-to heading
   never sits flush against the top of the screen. */
[id] { scroll-margin-top: 28px; }

/* Focus is visible and shaped like everything else here: a solid ink ring
   with a paper gap, never a browser default halo. */
:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: var(--r-chip);
}
.band--sky :focus-visible,
.band--ink :focus-visible { outline-color: var(--paper); }

::selection { background: var(--sun); color: var(--ink); }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 60;
  padding: 12px 20px;
  background: var(--leaf);
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  font-weight: 700;
  text-decoration: none;
}
.skip:focus { left: 16px; top: 16px; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   4. Layout
   -------------------------------------------------------------------------- */
.wrap {
  width: min(1140px, 100% - 3rem);
  margin-inline: auto;
}
@media (max-width: 640px) {
  .wrap { width: min(1140px, 100% - 2rem); }
}

/* Section grounds alternate between exactly two values. Nothing else is ever
   a section background, and no gradient is ever a section background. */
.band { padding-block: clamp(52px, 7.5vw, 92px); position: relative; }
.band--paper { background: var(--paper); }
.band--sand  { background: var(--sand); }
.band--sky   { background: var(--sky); }
.band--ink   { background: var(--ink); color: var(--paper); }

/* Section headers are left-aligned. A centred long Lithuanian headline wraps
   into a ragged pyramid, which is the fastest way a translated site looks
   broken. Only the final CTA centres, and its headline is capped at 4 words. */
.head { max-width: 62ch; margin-bottom: clamp(28px, 4vw, 44px); }
.head .lead { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   5. Type
   -------------------------------------------------------------------------- */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  margin: 0 0 .4em;
  text-wrap: balance;
}
h1 {
  font-size: clamp(32px, 6vw, 56px);
  line-height: 1.05;
  letter-spacing: -.5px;
}
h2 {
  font-size: clamp(26px, 4vw, 38px);
  line-height: 1.1;
  letter-spacing: -.25px;
}
h3 {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.25;
}

p { margin: 0 0 1em; max-width: 62ch; }

.lead {
  font-size: clamp(17px, 2vw, 20px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 62ch;
}

.eyebrow {
  display: inline-block;
  margin: 0 0 14px;
  padding: 5px 12px;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--paper);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: .08em;
  text-transform: uppercase;
  transform: rotate(-1.2deg);   /* stickers are never square to the page */
}

.fine { font-size: 14px; line-height: 1.5; color: var(--ink-soft); }

/* The swash: a hand-drawn sun-yellow stroke behind the accent words. Drawn,
   not a border-bottom — a straight rule under the words is the flat default. */
.accent {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='18' preserveAspectRatio='none'%3E%3Cpath d='M5 12c58-9 122-10 230-4' stroke='%23FFD43B' stroke-width='11' stroke-linecap='round' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 0 92%;
  background-size: 100% .32em;
  /* When the headline wraps, the accent becomes two line boxes. By default the browser
     SLICES one stroke across their union, so line 1 gets a thin rising stub cut square
     and line 2 a blunt-left one — the swash looks broken rather than hand-drawn. `clone`
     redraws the whole stroke on each fragment, which is what a pen would do. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* --------------------------------------------------------------------------
   6. Buttons — the candy edge, verbatim from the game
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 24px;
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 5px 0 var(--ink-edge), 0 14px 26px rgba(39, 65, 86, .16);
  transition: transform 180ms var(--spring), box-shadow 180ms var(--spring);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(3px); box-shadow: var(--edge-press); }

/* Leaf with INK text, not white. Measured ≈5.7:1, and it is exactly how the
   game's own buttons work. White on this green would be illegible. */
.btn--leaf { background: var(--leaf); }

/* The store row. Normally two official badges; the drawn .store button below
   is only the fallback for a store this repo ships no artwork for.

   The gap is also the badges' CLEAR SPACE, which is the one piece of the
   guidelines that is a LAYOUT rule rather than an artwork rule: Google asks
   for a quarter of the badge height free on every side, Apple for a tenth. At
   --badge-h: 56px that is 14px and 5.6px, so the 18px floor clears both — and
   it is why the gap is written against --badge-h instead of being left at a
   round number that would stop being enough if the badges ever grew. */
.stores {
  --badge-h: clamp(46px, 4.6vw, 56px);   /* the VISIBLE height of a badge */
  --badge-clear: max(18px, calc(var(--badge-h) * .3));

  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;   /* badges align on their top edge, chip or no chip */
  gap: var(--badge-clear);
  margin: 26px 0 16px;
  padding: 0;
  list-style: none;
}

/* Each item is a badge with its status under it. Column rather than a row so
   two badges stay side by side on a phone instead of each dragging a pill out
   to the right and wrapping. align-items keeps the chip centred on the badge
   in the hero AND in the centred final CTA, so one rule covers both. */
.stores li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--badge-clear);
}
.store {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px;
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  box-shadow: 0 5px 0 var(--ink-edge), 0 14px 26px rgba(39, 65, 86, .16);
  transition: transform 180ms var(--spring), box-shadow 180ms var(--spring);
}
.store svg { width: 26px; height: 26px; flex: none; }
.store b {
  display: block;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 800;
  line-height: 1.15;
}
a.store:hover { transform: translateY(-2px); }
a.store:active { transform: translateY(3px); box-shadow: var(--edge-press); }

/* Not-live store badges must not look pressable.
 *
 * They were carrying the same solid outline, the same fill and the same candy
 * edge as a real button and only a slightly flatter shadow — so they read as
 * clickable, and a parent who clicks one gets nothing. The state is now drawn
 * rather than merely dimmed: a DASHED outline (still 2px, still ink, so it
 * stays inside the system), no fill and no edge under it. Nothing is lifted
 * off the page, so nothing invites a press.
 *
 * The Netrukus badge keeps its solid ink outline and sun fill on purpose: it
 * becomes the only solid thing in the shape, which puts the eye exactly on the
 * word that explains why the rest is hollow.
 *
 * Padding is untouched, so the day the owner pastes the store URLs in and
 * these become real <a>s, nothing on the page moves. */
.store--soon {
  border-style: dashed;
  background: transparent;
  box-shadow: none;
  cursor: default;
}

/* The one thing a visitor CAN do while the badges are inert. */
.soon-link { margin: 0 0 14px; }
.soon-link a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 0;
  font-size: 15px;
  font-weight: 700;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.soon-link svg { width: 17px; height: 17px; flex: none; }
.store--soon small {
  display: inline-block;
  margin-top: 3px;
  padding: 1px 9px;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--sun);
  color: var(--ink);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.5;
}

/* Official store badges.
 *
 * The one thing on this page the house style is not allowed to touch. Google
 * and Apple both forbid recolouring the artwork, adding effects to it,
 * outlining it, squashing it, or letting anything intrude on its clear space —
 * so there is no ink border here, no candy edge, no shadow, no aspect-ratio
 * fiddling, and the hover is a lift and nothing else. Everything below is size
 * and position.
 *
 * Both badges are drawn to the same VISIBLE button height, --badge-h, which is
 * what makes a Google badge and an Apple badge read as a pair rather than as
 * two pasted images. That takes one number per FILE, because the vendors pad
 * their canvases differently and do not even agree with themselves between
 * languages (measured — see StoreLink::BADGES):
 *
 *   app-store-*.svg     119.66×40, edge to edge          → scale 1
 *   google-play-lt.png  646×250 canvas, button 646×192   → scale 250/192
 *   google-play-en.png  646×250 canvas, button 564×168   → scale 250/168
 *
 * So the IMAGE is drawn at --badge-h × --badge-scale (set inline per file by
 * the template) and its box is pinned to --badge-h, letting the transparent
 * margin overflow. Every file's opaque area is centred in its canvas, so the
 * overflow is symmetric and the visible buttons land on exactly the same
 * baseline no matter which pair of files is served. What overflows is
 * transparent — it is the badge's own clear space, so it adds to the gap
 * rather than eating it, and nothing is ever drawn in it.
 *
 * Apple's minimum is 40px tall and Google's is 60px wide. --badge-h never goes
 * below 46px, which at Apple's 2.99:1 is 138px wide and at Google's 3.36:1 is
 * 155px — clear of both floors on the narrowest phone. */
.store-badge {
  display: inline-flex;
  align-items: center;      /* centres the overflow, so the buttons align */
  justify-content: center;
  height: var(--badge-h);   /* the box IS the visible button */
  transition: transform 180ms var(--spring);
}
.store-badge img {
  height: calc(var(--badge-h) * var(--badge-scale, 1));
  /* Both beat the global img reset. `max-width` is deliberately NOT set: with
     a fixed height it would squash the artwork rather than scale it, and the
     widest badge is ~190px, so it cannot overflow a phone anyway — the row
     wraps first. */
  width: auto;
  max-width: none;
}

/* Same lift as .btn and .store, so the row behaves as one row. The global
   prefers-reduced-motion block at the end of this file already flattens the
   transition; there is no reason for this button to answer that differently
   from every other button on the page. */
.store-badge:hover { transform: translateY(-2px); }
.store-badge:active { transform: translateY(1px); }

/* Waiting for approval. The artwork is identical to the live state — the badge
   is never dimmed, greyed or restyled to say "not yet", because that would be
   altering it. The only difference is that it does not lift, does not show a
   pointer, and has the chip below it. */
.store-badge--soon { cursor: default; }
.store-badge--soon:hover,
.store-badge--soon:active { transform: none; }

/* The "Netrukus" chip. Same pill the drawn button used, now standing on its
   own outside the artwork. It sits a full clear-space below the badge, so it
   never encroaches on it. */
.store-soon {
  display: inline-block;
  padding: 2px 11px;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--sun);
  color: var(--ink);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   7. Header
   -------------------------------------------------------------------------- */
.site-head {
  position: relative;
  z-index: 30;
  background: var(--paper);
  padding-block: 16px;
}
.site-head .wrap {
  display: flex;
  align-items: center;
  gap: 14px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 800;
  text-decoration: none;
  white-space: nowrap;
}
/* 42, not 38: the mark now spends its top third on the crest that breaks the
   circle, so the badge itself is smaller than the old full-bleed disc. Sizing
   the SVG box up restores the optical weight the wordmark needs next to it. */
.brand svg { width: 42px; height: 42px; flex: none; }

/* An uploaded logo standing in for the drawn badge, at the same optical weight.
   HEIGHT IS FIXED AND WIDTH IS AUTO, which is the part that matters: the file
   is whatever the owner had to hand, and a 2000px original must not be able to
   take the header apart. Paired with the intrinsic width/height attributes the
   partial writes, width:auto also lets the browser work out the aspect ratio
   and reserve the box before the image arrives, so nothing jumps mid-load.

   max-width is the second half of that guarantee, for a logo that is wide
   rather than tall: past 190px it shrinks to fit instead of pushing the nav off
   the row. object-fit only matters once both bounds bite, and then it letterboxes
   rather than crops — a squashed logo is a logo nobody recognises. */
.brand-logo {
  height: 42px;
  width: auto;
  max-width: 190px;
  object-fit: contain;
  flex: none;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  flex-wrap: wrap;
}
.site-nav a {
  padding: 8px 12px;
  border-radius: var(--r-chip);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.site-nav a:hover { background: var(--sand); }

/* A menu item's dropdown — today only „Ko išmoks", whose children are the
   seven skill pages, editable on the Navigation screen. The wrapper exists
   only when there ARE children; an item without any renders the same bare
   <a class="nav-anchor"> it always did, so none of this applies to it.

   OPENS ON :hover AND :focus-within, no script. Hover is the mouse;
   focus-within is the keyboard — the moment Tab lands on the label the list
   appears, and the next Tabs walk straight through its links in document
   order. The label itself stays a plain link to the front-page section: the
   dropdown is the WRAPPER's behaviour, so hovering opens the list without
   costing the label the click it has always had.

   The card is the language switcher's, on purpose (.langs > ul, above): same
   paper, same ink border, same shadow, same pill hover on the rows. Two
   different-looking dropdowns in one header would read as two systems. */
.nav-drop { position: relative; }

.nav-drop > .nav-anchor { display: inline-flex; align-items: center; gap: 5px; }

.nav-drop-caret { width: 13px; height: 13px; transition: transform .18s var(--spring); }
.nav-drop:hover .nav-drop-caret,
.nav-drop:focus-within .nav-drop-caret { transform: rotate(180deg); }

.nav-drop-list {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 30;
  min-width: 210px;
  margin: 8px 0 0;
  padding: 6px;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  box-shadow: var(--edge);
  list-style: none;
}
.nav-drop-list a { display: block; }

@media (min-width: 721px) {
  .nav-drop:hover > .nav-drop-list,
  .nav-drop:focus-within > .nav-drop-list { display: block; }

  /* The card sits 8px below the item, and that 8px is not the wrapper's box —
     a pointer crossing it would end the hover and shut the list before it can
     be reached. This invisible strip spans the gap so the whole journey from
     label to card stays inside :hover. Only drawn while open, so it cannot
     sit over whatever is under the header the rest of the time. */
  .nav-drop:hover::after,
  .nav-drop:focus-within::after {
    content: '';
    position: absolute;
    inset: 100% 0 -8px;
  }

  /* The caret BUTTON is the phone's control (below). On desktop the CSS-only
     pattern needs no button, so it leaves the accessibility tree entirely
     rather than offering keyboard users a control the hover already covers. */
  .nav-drop-toggle { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .nav-drop-caret { transition: none; }
}

/* Language switcher: one flag that opens a list.

   Was five uppercase codes in a pill, which read as a second navigation
   competing with the real one — and it grows worse with every language. A flag
   is understood without reading, in any of the languages on offer, which is the
   one place on the page where that actually matters.

   <details>/<summary> rather than a button and a script: it opens on click and
   on Enter, closes on Escape, is announced as a disclosure by screen readers,
   and works before any JavaScript loads. site.js only adds click-outside. */
.langs { position: relative; margin: 0 0 0 8px; }

.langs > summary {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 9px 4px 5px;
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  cursor: pointer;
  list-style: none;                 /* Firefox */
  user-select: none;
}
.langs > summary::-webkit-details-marker { display: none; }
.langs > summary:hover { background: var(--sand); }

.flag {
  width: 22px;
  height: auto;
  aspect-ratio: 3 / 2;
  border-radius: 3px;
  /* A hairline, not a border: a white flag (PL) on warm paper would otherwise
     have no edge on its top half and read as a floating red bar. */
  box-shadow: 0 0 0 1px rgba(39, 65, 86, .22);
  flex: 0 0 auto;
}

.langs-now { font-size: 14px; font-weight: 700; text-transform: uppercase; }
.langs-caret { width: 14px; height: 14px; transition: transform .18s var(--spring); }
.langs[open] > summary .langs-caret { transform: rotate(180deg); }

.langs > ul {
  position: absolute;
  right: 0;
  z-index: 30;
  min-width: 190px;
  margin: 8px 0 0;
  padding: 6px;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  box-shadow: var(--edge);
  list-style: none;
}
.langs > ul a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-chip);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}
.langs > ul a:hover { background: var(--sand); }
.langs > ul a[aria-current] { background: var(--leaf); }

/* Only the owner sees these — a language he has switched on but not finished. */
.langs-draft {
  margin-left: auto;
  padding: 2px 7px;
  background: var(--sun);
  border-radius: var(--r-pill);
  font-size: 11px;
  font-style: normal;
  font-weight: 700;
  text-transform: lowercase;
}

@media (prefers-reduced-motion: reduce) {
  .langs-caret { transition: none; }
}

/* Below 720 the nav drops to its own full-width row under the brand and turns
   into a scrollable strip of chips.
 *
 * It used to hide the in-page anchors here on the theory that "the page itself
 * is the navigation". Measured, that left a 390px phone with exactly ONE link
 * in the header — the privacy policy — on an 8,403px page. The page is only
 * the navigation if you are willing to scroll all of it, and the parent
 * looking for the price is not.
 *
 * nowrap + overflow-x, not flex-wrap: five chips wrap into a ragged two-line
 * block that shoves the hero down. Scrolling keeps the header one row tall and
 * costs nothing when they happen to fit. The row is a flex item at width:100%,
 * so the scroll is contained and cannot push the document sideways. */
@media (max-width: 720px) {
  .site-head .wrap { flex-wrap: wrap; }
  .site-nav {
    width: 100%;
    margin-left: 0;
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* Room for the candy edge under the chips, which would otherwise be
       clipped by the scroll container. */
    padding-bottom: 6px;
  }
  /* Direct children only. `.site-nav a` would also catch the language links
     inside .langs, which have their own pill treatment and would each become
     a chip inside a chip the day a second language is switched on. */
  .site-nav > a {
    flex: none;
    padding: 9px 14px;
    border: 2px solid var(--ink);
    border-radius: var(--r-chip);
    background: var(--paper);
    box-shadow: var(--edge-sm);
    font-size: 14px;
  }
  .site-nav > a:hover { background: var(--sand); }
  .langs { flex: none; margin-left: 0; }

  /* The dropdown, in the strip. WHY IT EXPANDS SIDEWAYS RATHER THAN DROPPING
     A CARD OR INDENTING A SUB-LIST: this nav is a single horizontally
     scrolling row inside overflow-x:auto, and per spec that forces
     overflow-y out of `visible` too — an absolutely-positioned card would be
     CLIPPED at the strip's edge, not float over the page. And an
     always-visible indented sub-list has no vertical run to indent into; the
     strip's whole design (see the note above) is that the header stays one
     row tall. So the children expand IN the row, as more chips after the
     parent — the shape the strip is already made of, reached by the scrolling
     it already does, with nothing to clip and nothing to hover.

     THE TAP TARGETS: the chip is two controls. The label keeps its anchor
     behaviour untouched — tapping „Ko išmoks" still jumps to the section —
     and the CARET is a real button (44px of it, no dead zone between the
     two) that unfolds the chips. Hover exists on no phone, so this is the
     one place the toggle earns its few lines of JS; see site.js. */
  /* gap: 0, not the strip's 8px — the label and the caret are the two halves
     of ONE chip, and a gap between them would be a seam of page background
     splitting it. The children keep their 8px rhythm via the list's own gap
     and margin below. */
  .nav-drop { flex: none; display: flex; align-items: stretch; gap: 0; position: static; }

  /* The label + caret pair drawn as ONE chip, matching its .site-nav > a
     siblings — the border moves from the links to the wrapper here. */
  .nav-drop > .nav-anchor,
  .nav-drop-toggle {
    border: 2px solid var(--ink);
    background: var(--paper);
    box-shadow: var(--edge-sm);
  }
  .nav-drop > .nav-anchor {
    padding: 9px 12px 9px 14px;
    border-right: 0;
    border-radius: var(--r-chip) 0 0 var(--r-chip);
    font-size: 14px;
  }
  /* The desktop caret inside the label yields to the button beside it — two
     carets on one chip would promise two menus. */
  .nav-drop > .nav-anchor .nav-drop-caret { display: none; }

  .nav-drop-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    margin: 0;
    padding: 0 12px;
    border-left-width: 1px;
    border-radius: 0 var(--r-chip) var(--r-chip) 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
  }
  .nav-drop-toggle:hover { background: var(--sand); }
  .nav-drop-toggle svg { width: 14px; height: 14px; transition: transform .18s var(--spring); }
  .nav-drop[data-open] .nav-drop-toggle svg { transform: rotate(180deg); }

  /* Closed: nothing. Open: the children join the row as chips of their own,
     styled exactly like their .site-nav > a siblings. The strip's own
     horizontal scroll absorbs the extra width, so nothing overflows the
     page at 390px — same as when the plain chips already outgrow it. */
  .nav-drop-list { display: none; }
  .nav-drop[data-open] > .nav-drop-list {
    display: flex;
    gap: 8px;
    position: static;
    min-width: 0;
    margin: 0 0 0 8px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .nav-drop-list li { flex: none; display: flex; }
  .nav-drop-list a {
    flex: none;
    padding: 9px 14px;
    border: 2px solid var(--ink);
    border-radius: var(--r-chip);
    background: var(--paper);
    box-shadow: var(--edge-sm);
    font-size: 14px;
  }

  @media (prefers-reduced-motion: reduce) {
    .nav-drop-toggle svg { transition: none; }
  }
}

/* --------------------------------------------------------------------------
   8. Hero
   -------------------------------------------------------------------------- */
.hero { padding-block: clamp(20px, 3.5vw, 44px) clamp(60px, 8vw, 108px); }
.hero-grid {
  display: grid;
  gap: clamp(44px, 6vw, 64px);
  align-items: center;
}
/* The two-column hero starts at 1080, not 900, and eases into its final
   proportion at 1200.
 *
 * Measured, at every step of the old 900–1079 band: the text column resolved
 * to 371–441px while the h1 was already at its 54–56px cap, so the headline
 * broke into FIVE lines 294px tall and the hand-drawn swash tore across two of
 * them — the worst layout on the site, and it covered every common laptop
 * width. Single column at those widths gives the same headline two lines and
 * 108px. The floor is the text column: 441px gives five lines, 460px gives
 * four, so the columns only engage once there is enough width to stay above
 * that, and the art column is held back to 1.05fr until 1200 so it cannot eat
 * the difference. Above 1200 it returns to the 1.15fr the art wants. */
@media (min-width: 1080px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}
@media (min-width: 1200px) {
  /* The art column is the wider one: the hero screenshot is the exception to
     the sizing everywhere else, and 844×390 art needs width to read at all. */
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
}
.hero h1 { margin-bottom: .35em; }
.hero .note {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
}

/* The screenshot frame is the sticker, not a device. The assets are 844×390
   landscape — a phone mockup would be a lie about what this app looks like. */
.frame {
  position: relative;
  border: 2px solid var(--ink);
  border-radius: var(--r-frame);
  background: var(--paper);
  padding: 10px;
  box-shadow: 0 8px 0 var(--ink-edge), 0 20px 34px rgba(39, 65, 86, .14);
}
.frame img {
  width: 100%;
  border-radius: var(--r-chip);
}

.hero-art { position: relative; }
.hero-art > .frame { transform: rotate(-2deg); }

/* Dinas breaks out over the bottom-left corner. A mascot overlapping the
   frame edge is the single most hand-made gesture available on a page, and
   it is the one thing a template cannot fake. */
.dinas {
  position: absolute;
  left: 0;
  bottom: clamp(-48px, -3.4vw, -22px);
  width: clamp(94px, 12.5vw, 148px);
  z-index: 2;
  pointer-events: none;
  /* Offset by a share of HIS OWN width, not a fixed pixel count, so the
     proportion hanging off the corner is the same at every screen size. */
  transform: translateX(-12%);
}
@media (min-width: 1080px) {
  /* Two-column hero: he can hang a third of the way out into the column gap.
     38% is the most the gap allows before he starts crowding the lead text.
     Tracks the grid breakpoint above — in one column there is no gap to hang
     into and he would sit on the lead paragraph instead. */
  .dinas { transform: translateX(-38%); }
}
.dinas svg, .story-art svg, .final-art svg, .oops-art svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;   /* the sway must not clip his spikes at the viewBox */
}

/* --------------------------------------------------------------------------
   9. Trust strip — a pre-launch app has no awards, so the promises are the
   credentials. Ink-outlined chips, tilted, wrapping.
   -------------------------------------------------------------------------- */
.facts {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.fact {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  /* basis auto, not a fixed 250px: the chips end up different widths because
     their sentences are different lengths, which is the point. Four equal
     boxes in a row would be the grid this page is trying not to be. */
  flex: 1 1 auto;
  min-width: 232px;
  max-width: 100%;
  padding: 16px 20px;
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--paper);
  box-shadow: var(--edge-sm);
}
.fact:nth-child(odd)  { transform: rotate(-.7deg); }
.fact:nth-child(even) { transform: rotate(.6deg); }
.fact b {
  display: block;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  line-height: 1.2;
}
.fact span:not(.chip) { display: block; font-size: 15px; line-height: 1.45; color: var(--ink-soft); }

/* Icon chips — a 44px tile, inside the 48px cap.
 *
 * The background used to be the candy colour at full strength, with a thin ink
 * glyph drawn on top of it. Two things were wrong with that. Dark ink on a
 * mid-saturation fill is the weakest contrast pairing on the page, and more to
 * the point it left no room for the icon to be COLOURED — so the icons had to
 * be line drawings, which is exactly the generic look the redraw was for.
 *
 * So the tile is now a pale wash of the chosen colour and the drawing inside
 * carries the saturation. That is not a retreat from Rule 4, it is the game's
 * own arrangement: the category tiles in the app are pale (#FFE0EC, #FFE3A3,
 * #D6F0FF …) precisely so the little scene on top can be full-strength candy
 * without the two fighting. Several of the values below ARE those tiles.
 *
 * Written as literal hex rather than color-mix() so there is nothing to fall
 * back from, and picked by hand rather than generated: a flat percentage of
 * white sends yellow chalky and purple grey at the same setting.
 *
 * data-color therefore still means what the owner thinks it means when he picks
 * it in the admin — it is the tile — while the icon keeps its own palette.
 */
.chip {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--paper);
  color: var(--ink);   /* every icon outlines in currentColor — see partials/icon.blade.php */
  transform: rotate(-3deg);
}
/* 30px, not the 24px this was drawn at. The icons are miniature scenes rather
   than glyphs now, and six units of extra width is the difference between the
   drum reading as a drum and reading as a red smudge. 30 + the 2px border
   either side still leaves 5px of tile showing all round, so the drawing sits
   ON the chip instead of bursting out of it. */
.chip svg { width: 30px; height: 30px; }
.chip[data-color="leaf"]   { background: #DDF6D6; }
.chip[data-color="sky"]    { background: #D6F0FF; }
.chip[data-color="coral"]  { background: #FFDFDB; }
.chip[data-color="sun"]    { background: #FFEFB8; }
.chip[data-color="orange"] { background: #FFE0C2; }
.chip[data-color="teal"]   { background: #D2F5EA; }
.chip[data-color="blue"]   { background: #D9EBFF; }
.chip[data-color="purple"] { background: #E5DBFF; }
.chip[data-color="pink"]   { background: #FFE0EC; }

/* --------------------------------------------------------------------------
   10. The father's story — a pull quote, not a card
   -------------------------------------------------------------------------- */
.story {
  display: grid;
  gap: clamp(22px, 4vw, 44px);
  align-items: center;
}
@media (min-width: 860px) {
  .story { grid-template-columns: 180px minmax(0, 1fr); }
}
.story-art { justify-self: start; width: clamp(120px, 17vw, 180px); }
.story blockquote {
  margin: 0;
  padding-left: clamp(18px, 3vw, 28px);
  border-left: 6px solid var(--sun);
}
/* Only the FIRST paragraph is the pull quote. This used to be `blockquote p`,
   which at (0,1,2) also beat `.lead` at (0,1,0) — so the moment the story ran
   to more than one paragraph the body text would have been set in 30px Baloo
   as well, and the section would have been one undifferentiated shout. */
.story blockquote > p:first-of-type {
  font-family: var(--font-display);
  font-size: clamp(21px, 3vw, 30px);
  font-weight: 700;
  line-height: 1.3;
  max-width: 34ch;
}
.story blockquote .lead {
  font-size: 17px;
  line-height: 1.6;
  max-width: 56ch;
}
.story blockquote .lead:last-of-type { margin-bottom: 1.2em; }
.story cite {
  display: block;
  font-size: 15px;
  font-style: normal;
  font-weight: 600;
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   11. Learn cards — deliberately unequal. Two per row at different widths,
   because a row of three identical tiles is the generator's signature.
   -------------------------------------------------------------------------- */
.cards {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(18px, 2.6vw, 26px);
  /* start, not the grid default stretch: a card with one line of text should
     BE one line tall. Stretching every card to its neighbour is what makes a
     set of cards read as a table of equal tiles. */
  align-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}
.cards > * { grid-column: span 12; }
@media (min-width: 820px) {
  .cards > *:nth-child(6n+1) { grid-column: span 7; }
  .cards > *:nth-child(6n+2) { grid-column: span 5; }
  .cards > *:nth-child(6n+3) { grid-column: span 5; }
  .cards > *:nth-child(6n+4) { grid-column: span 7; }
  .cards > *:nth-child(6n+5) { grid-column: span 7; }
  .cards > *:nth-child(6n+6) { grid-column: span 5; }
}
.card {
  padding: clamp(20px, 2.6vw, 26px);
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--paper);
  box-shadow: var(--edge);
  transition: transform 180ms var(--spring);
}
.card:hover { transform: translateY(-2px); }
.card h3 { margin: 14px 0 .3em; }
.card p { margin: 0; font-size: 16px; line-height: 1.55; color: var(--ink-soft); }

/* A card that leads somewhere (the learn cards point at their skill pages).
   The anchor lives on the heading and its ::after stretches over the whole
   card, so the entire surface is one tap target and one Tab stop with zero
   change to the card's layout. Hover shows the same 2px underline every
   other text link on the site shows (.prose a, .post h2 a), on top of the
   lift .card:hover already does; focus draws the site's standard ink ring,
   but around the CARD — a ring around three words of heading would say the
   words are the target when the whole card is. */
.card--link { position: relative; }
.card-link { color: inherit; text-decoration: none; }
.card-link::after { content: ""; position: absolute; inset: 0; border-radius: var(--r-card); }
.card--link:hover .card-link { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.card-link:focus-visible { outline: none; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.card-link:focus-visible::after { outline: 3px solid var(--ink); outline-offset: 3px; border-radius: var(--r-card); }

/* --------------------------------------------------------------------------
   12. Gallery — the proof section. Rotated, overlapping sticker frames.
   -------------------------------------------------------------------------- */
.shots {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(30px, 4vw, 46px) clamp(26px, 3.4vw, 40px);
  margin: 0;
  padding: 0;
  list-style: none;
}
.shot { margin: 0; position: relative; }
.shot .frame { transition: transform 220ms var(--spring); }

/* Rotation lives on the frame, never on the caption — tilted body text is
   unreadable, and that is the point where "hand-made" becomes "broken". */
.shot:nth-child(5n+1) .frame { transform: rotate(-2deg); }
.shot:nth-child(5n+2) .frame { transform: rotate(1.5deg); }
.shot:nth-child(5n+3) .frame { transform: rotate(-1deg); }
.shot:nth-child(5n+4) .frame { transform: rotate(2deg); }
.shot:nth-child(5n+5) .frame { transform: rotate(-1.5deg); }
.shot .frame:hover { transform: rotate(0deg) translateY(-3px); }

.shot figcaption { padding: 16px 4px 0; }
.shot figcaption b {
  display: block;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 800;
  line-height: 1.2;
}
.shot figcaption span {
  display: block;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 46ch;
}

@media (min-width: 720px) {
  /* Frames lean into each other instead of sitting in a clean grid. The pull
     has to BEAT the column gap, not just cancel it: at -20px each the two
     margins exactly ate the 40px gap and the frames merely touched. -30px
     each leaves a real ~20px overlap, which is what makes them read as
     stacked stickers. The pull is inward only, so it can never cause
     horizontal overflow, and the odd column stays on top of the pile. */
  .shot:nth-child(odd)  { margin-right: -26px; z-index: 2; }
  .shot:nth-child(even) { margin-left: -26px; margin-top: clamp(28px, 4vw, 56px); z-index: 1; }
}
@media (max-width: 719px) {
  /* Below 720 they stack straight and the tilt drops to a hint, so nothing
     clips the viewport edge. */
  .shots { grid-template-columns: minmax(0, 1fr); }
  .shot:nth-child(odd)  .frame { transform: rotate(-.75deg); }
  .shot:nth-child(even) .frame { transform: rotate(.75deg); }
}

/* The rest of the gallery.
 *
 * A native <details>, drawn as one of the site's own buttons, because the
 * summary already behaves like a button for a keyboard and a screen reader and
 * this site has no build step to spend on rebuilding that. The marker is
 * removed in two ways because Safari still wants the WebKit pseudo-element. */
.shots-more {
  margin-top: clamp(30px, 4vw, 46px);
  text-align: center;          /* centres the inline-flex summary below */
}
.shots-more .shots { text-align: left; }
.shots-more > summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 24px;
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--paper);
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 800;
  line-height: 1.2;
  cursor: pointer;
  list-style: none;                 /* Firefox, Chrome */
  box-shadow: 0 5px 0 var(--ink-edge), 0 14px 26px rgba(39, 65, 86, .16);
  transition: transform 180ms var(--spring), box-shadow 180ms var(--spring);
}
.shots-more > summary::-webkit-details-marker { display: none; }   /* Safari */
.shots-more > summary:hover { transform: translateY(-2px); }
.shots-more > summary:active { transform: translateY(3px); box-shadow: var(--edge-press); }

/* The count, and the chevron that turns when the section is open. Both are
   drawn rather than written, so neither needs translating. */
.shots-more-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 7px;
  border-radius: var(--r-pill);
  background: var(--sun);
  font-size: 14px;
  line-height: 1;
}
.shots-more > summary::after {
  content: "";
  width: 13px;
  height: 13px;
  border-right: 3px solid currentColor;
  border-bottom: 3px solid currentColor;
  border-radius: 2px;
  transform: translateY(-3px) rotate(45deg);
  transition: transform 220ms var(--spring);
}
.shots-more[open] > summary::after { transform: translateY(2px) rotate(-135deg); }

/* Opening it starts a second grid, so the frames keep leaning on each other
   exactly as they do above — nth-child restarts, which is what keeps the
   left/right lean alternating instead of doubling up at the seam. The margin
   is the gap between the button and the first new row. */
.shots-more[open] .shots { margin-top: clamp(30px, 4vw, 46px); }

/* --------------------------------------------------------------------------
   13. Parents — a checklist, not more cards
   -------------------------------------------------------------------------- */
.plist {
  display: grid;
  gap: clamp(18px, 2.4vw, 26px) clamp(24px, 4vw, 48px);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 760px) { .plist { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.plist li { display: flex; gap: 14px; align-items: flex-start; }
.plist b {
  display: block;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  line-height: 1.25;
}
.plist span:not(.chip) { display: block; font-size: 16px; line-height: 1.5; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   14. FAQ
   -------------------------------------------------------------------------- */
.faq { display: grid; gap: 14px; max-width: 78ch; }
.faq details {
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--paper);
  box-shadow: var(--edge-sm);
}
.faq summary {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 22px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 800;
  line-height: 1.3;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  flex: none;
  width: 22px;
  height: 22px;
  margin-left: auto;
  background: var(--ink);
  transition: transform 180ms var(--spring);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}
.faq details[open] summary::after { transform: rotate(180deg); }
.faq .answer { padding: 0 22px 20px; }
.faq .answer p { margin: 0; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   15. Contact
   -------------------------------------------------------------------------- */
.contact-grid { display: grid; gap: clamp(28px, 4vw, 52px); align-items: start; }
@media (min-width: 860px) { .contact-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); } }

.form { display: grid; gap: 16px; }
.field { display: grid; gap: 6px; }
.field label { font-size: 15px; font-weight: 700; }
.field input, .field textarea {
  width: 100%;
  padding: 12px 16px;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
}
.field textarea { resize: vertical; min-height: 132px; }
.field em {
  font-style: normal;
  font-size: 14px;
  font-weight: 600;
  color: #A32020;   /* ink-family red, dark enough to pass AA on sand */
}
.field--bad input, .field--bad textarea { border-color: #A32020; }

.notice {
  margin: 0 0 22px;
  padding: 14px 18px;
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--leaf);
  font-weight: 600;
  max-width: none;
  box-shadow: var(--edge-sm);
}

/* The honeypot. Hidden off-screen rather than display:none — some bots skip
   display:none fields, which defeats the whole trap. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   16. Final CTA — the page's one saturated ground, and the one seam
   -------------------------------------------------------------------------- */
/* The seam has to be painted in the tone of the section ABOVE it. The hills
   are filled sky and the area over them is transparent, so on the body's own
   paper ground it left a cream stripe between a sand section and the sky.
   The blade passes the preceding section's tone down as seam--paper/--sand. */
.seam { display: block; line-height: 0; }
.seam--paper { background: var(--paper); }
.seam--sand  { background: var(--sand); }
.horizon {
  display: block;
  width: 100%;
  height: auto;
  margin-bottom: -2px;   /* eat the sub-pixel seam against the sky band */
}
.final { text-align: center; padding-top: clamp(16px, 2vw, 28px); }
.final h2 { margin-inline: auto; max-width: 16ch; }
.final .lead { margin-inline: auto; color: var(--ink); }
.final .stores { justify-content: center; }
.final-art { width: clamp(116px, 15vw, 164px); margin: 0 auto 6px; }

/* --------------------------------------------------------------------------
   16b. Launch alert — the only conversion the site has before launch day

   A paper card on the sky ground: the same sticker treatment as every other
   card, which is what stops a form on a saturated band reading as a banner ad.
   The section centres its text; the form must not, so this card resets it —
   left-aligned labels are the only ones a person can actually scan down.
   -------------------------------------------------------------------------- */
.notify {
  max-width: 620px;
  margin: clamp(30px, 4vw, 44px) auto 0;
  padding: clamp(22px, 3vw, 30px);
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--paper);
  box-shadow: var(--edge);
  text-align: left;
}
.notify h3 { font-size: 22px; margin-bottom: .2em; }
.notify-lead {
  margin: 0 0 18px;
  max-width: none;
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.notify .field { margin: 0; }
.notify .notice { margin-bottom: 0; }
.notify-fine { margin: 14px 0 0; }

/* Name and email share a row from 560 up; the button always takes its own,
   full width. A submit squeezed into a third column ends up narrower than the
   thumb aiming at it. */
.notify-row { display: grid; gap: 14px; }
@media (min-width: 560px) {
  .notify-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .notify-row .field--go { grid-column: 1 / -1; }
}
.notify-row .field--go .btn { width: 100%; justify-content: center; }

/* --------------------------------------------------------------------------
   17. Footer
   -------------------------------------------------------------------------- */
.site-foot { padding-block: clamp(44px, 6vw, 68px); }
.site-foot .brand { color: var(--paper); }
.foot-grid { display: grid; gap: 30px; align-items: start; }
@media (min-width: 760px) {
  .foot-grid { grid-template-columns: minmax(0, 1.3fr) repeat(2, minmax(0, 1fr)); }
}
.site-foot h2 {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .72;
  margin-bottom: 1em;
}
.foot-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; justify-items: start; }
/* padding-block, not a bigger gap: it grows the TARGET past the 24px minimum
   rather than just the space around it, which is what a thumb actually needs. */
.foot-list a { font-size: 15px; text-decoration: none; padding-block: 5px; }
.foot-list a:hover { text-decoration: underline; }
/* A footer link the owner has marked as a button — one call to action in a
   column of navigation. Outlined rather than filled: the footer band is already
   ink-coloured, and a solid brand-green pill on it reads as an advertisement
   dropped into the page rather than as part of it. No underline on hover, since
   the border is doing that job. */
.foot-btn {
  display: inline-block;
  margin-top: 4px;
  padding: 8px 16px;
  border: 2px solid rgba(253, 246, 236, .5);
  border-radius: var(--r-pill);
  font-weight: 700;
}
.foot-list a.foot-btn:hover { text-decoration: none; border-color: var(--paper); background: rgba(253, 246, 236, .1); }

/* The same idea in the header: one of the owner's own menu links, marked as a
   button. Leaf with INK text, exactly like .btn--leaf and like the game's own
   buttons — white on this green is illegible, which is the note that lives on
   .btn--leaf and is worth not re-learning. Smaller than a real .btn: this sits
   in a row of 15px links and must not make the bar taller than they do. */
.site-nav a.nav-btn {
  padding: 8px 16px;
  border: 2px solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--leaf);
  color: var(--ink);
}
.site-nav a.nav-btn:hover { background: var(--leaf); text-decoration: none; transform: translateY(-1px); }
.site-foot .fine { color: rgba(253, 246, 236, .66); max-width: 72ch; }
.site-foot .tagline { color: rgba(253, 246, 236, .82); font-size: 15px; max-width: 34ch; }
.socials { display: flex; gap: 10px; padding: 0; margin: 0; list-style: none; }
.socials a {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 2px solid var(--paper);
  border-radius: var(--r-pill);
}
.socials svg { width: 20px; height: 20px; }
.foot-rule { height: 2px; margin: 34px 0 20px; background: rgba(253, 246, 236, .22); }

/* --------------------------------------------------------------------------
   18. Legal / rich-text pages — the DB HTML, styled as part of the site
   -------------------------------------------------------------------------- */
.prose { max-width: 70ch; }
.prose h2 { font-size: clamp(22px, 3vw, 28px); margin-top: 1.7em; }
.prose h3 { margin-top: 1.5em; }
.prose h4 { font-family: var(--font-display); font-size: 18px; margin-top: 1.3em; }
.prose p, .prose li { color: var(--ink-soft); }
.prose li { margin-bottom: .5em; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose strong, .prose b { color: var(--ink); font-weight: 700; }
.prose a { font-weight: 600; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.prose hr { height: 2px; border: 0; background: var(--ink-edge); margin: 2em 0; }

/* The summary callout — this was a .box panel in the original HTML and it
   should still read as a highlighted panel, not a quiet quote. */
.prose blockquote {
  margin: 1.7em 0;
  padding: 20px 24px;
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--sand);
  box-shadow: var(--edge-sm);
}
.prose blockquote p { color: var(--ink); }
.prose blockquote > :last-child { margin-bottom: 0; }

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.5em 0;
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  overflow: hidden;
}
.prose th, .prose td {
  padding: 10px 14px;
  text-align: left;
  border-bottom: 2px solid var(--ink-edge);
}
.prose thead th { background: var(--sand); font-weight: 700; }
.prose tbody tr:last-child td { border-bottom: 0; }

.page-head { max-width: 70ch; }
.page-head .meta { font-size: 14px; color: var(--ink-soft); margin: 0; }

/* --------------------------------------------------------------------------
   19. 404
   -------------------------------------------------------------------------- */
.oops { max-width: 58ch; }
.oops-art { width: clamp(140px, 20vw, 196px); margin-bottom: 10px; }

/* --------------------------------------------------------------------------
   20. Motion — four moments, and that is the whole list.
   -------------------------------------------------------------------------- */

/* 2. Hero screenshot, on load, once. Not tied to scroll. The rotation is
      carried through every keyframe or the frame would un-tilt as it lands. */
@keyframes hero-in {
  from { opacity: 0; transform: rotate(-2deg) translateY(14px) scale(.985); }
  to   { opacity: 1; transform: rotate(-2deg) translateY(0) scale(1); }
}
.hero-art > .frame { animation: hero-in 420ms var(--spring) both; }

/* 3. Dinas idle — a slow sway on the body and head only, hinged low so he
      leans rather than spins. Ambient life: he never bounces, never waves
      back, never follows a cursor. transform-box: fill-box re-bases the
      origin onto each group's own box, so it can be stated as a percentage. */
@keyframes dinas-sway {
  0%, 100% { transform: rotate(-2deg); }
  50%      { transform: rotate(2deg); }
}
.dinas .m-body, .dinas .m-head,
.story-art .m-body, .story-art .m-head,
.final-art .m-body, .final-art .m-head,
.oops-art .m-body, .oops-art .m-head {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: dinas-sway 4s ease-in-out infinite;
}

/* 4. Gallery reveal — driven by the observer in site.js, 60ms stagger, once.
      The hidden state is applied by JS, never by this stylesheet: an element
      hidden by CSS stays hidden forever if the script fails to run. */
.shot[data-reveal] { opacity: 0; transform: translateY(8px); }
.shot[data-reveal="in"] {
  opacity: 1;
  transform: none;
  transition: opacity 420ms ease, transform 420ms var(--spring);
}

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

/* --------------------------------------------------------------------------
   News / updates

   Appended as its own block at the end of the file on purpose, so adding to
   this stylesheet never collides with edits happening above it.

   The house materials stay — 2px ink outline, candy edge, spring lift — but
   the tilt does not. A gallery of screenshots is a scrapbook and should look
   handled; a dated list of updates is a changelog, and a changelog that
   performs is a changelog nobody believes. Same paper, quieter voice.
   -------------------------------------------------------------------------- */
.posts {
  display: grid;
  gap: clamp(18px, 2.6vw, 26px);
  margin: 0;
  padding: 0;
  list-style: none;

  /* Centred in the wrap, like the menu above it — the owner's own words. At
     the full 1140px a card is a strip of text with a third of itself empty,
     and the whole page reads as pushed against the left margin. 880px keeps
     the side-by-side cover layout comfortable and puts the column in the
     middle, the way the home page's content sits. margin-inline, so below
     880px this is simply the wrap again and nothing changes on a phone. */
  max-width: 880px;
  margin-inline: auto;
}

/* The archive page's own header and feed link move with the column — a
   centred list under a left-pinned heading reads as two pages fighting. Both
   keep their left-aligned TEXT (see the .head note above: centred Lithuanian
   headlines wrap into pyramids); it is the block that centres, not the words.
   Scoped: .news-archive is only the index page, and `.posts +` only matches a
   link directly under a list — so the post page's 70ch prose measure and its
   back-link stay exactly where they were. */
.news-archive .page-head { max-width: 880px; margin-inline: auto; }
.posts + .news-feed { max-width: 880px; margin-inline: auto; }

.post { margin: 0; }
.post article {
  border: 2px solid var(--ink);
  border-radius: var(--r-card);
  background: var(--paper);
  box-shadow: var(--edge);
  overflow: hidden;                /* the cover corners follow the card */
  transition: transform 180ms var(--spring);
}
.post article:hover { transform: translateY(-2px); }

/* The cover only becomes a side column when there is room for both it and a
   readable measure. Below that it stacks, which is where most of these
   visitors are. */
@media (min-width: 720px) {
  .post article:has(.post-shot) {
    display: grid;
    grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
    align-items: stretch;
  }
}

.post-shot { display: block; background: var(--sand); }
.post-shot img {
  width: 100%;
  height: 100%;
  min-height: 160px;
  object-fit: cover;               /* a thumbnail, not the whole picture */
  display: block;
}

.post-text { padding: clamp(18px, 2.4vw, 26px); }

.post-date {
  margin: 0 0 6px;
  font-size: 14px;
  color: var(--ink-soft);
}
.post-date a { color: var(--ink-soft); }

/* h2 on the archive, where the page heading is the h1; h3 inside a news block
   on a built page, where the block's own heading is the h2. Same card, and the
   heading level has to follow the document rather than the drawing — a level
   skipped is a level a screen reader reports as a gap in the outline. */
.post h2, .post h3 { margin: 0 0 .35em; font-size: clamp(20px, 2.4vw, 26px); line-height: 1.2; }

/* The whole heading is the link. Underlined only on hover so a list of five
   posts is not five underlined blocks. */
.post h2 a, .post h3 a { color: var(--ink); text-decoration: none; }
.post h2 a:hover, .post h2 a:focus-visible,
.post h3 a:hover, .post h3 a:focus-visible { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }

.post-text p { margin: 0; font-size: 16px; line-height: 1.55; color: var(--ink-soft); }

/* Single post ---------------------------------------------------------- */
.post-cover { margin: clamp(22px, 3vw, 32px) 0; max-width: 760px; }
.post-cover img { display: block; height: auto; }

.post-more { margin-top: clamp(34px, 4.5vw, 52px); }
.post-more h2 { font-size: clamp(19px, 2.2vw, 23px); margin: 0 0 .6em; }

.post-more-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.post-more-list li {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  align-items: baseline;
  padding-bottom: 10px;
  border-bottom: 2px solid var(--ink-edge);
}
.post-more-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.post-more-list a { font-weight: 600; }
.post-more-list time { font-size: 14px; color: var(--ink-soft); }

.news-feed { margin: clamp(26px, 3.4vw, 38px) 0 0; font-size: 15px; }

/* Images placed inside admin-written content, which is the whole reason the
   media library exists. Constrained here rather than trusted to the pasted
   markup: the owner copies a path, not a width, and a 2400px screenshot in the
   privacy policy would otherwise push the page sideways on a phone. */
.prose img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 1.6em 0;
  border: 2px solid var(--ink);
  border-radius: var(--r-chip);
}

/* Pictures placed with the editor arrive wrapped:

     <figure class="image image_resized" style="width:50%"><img …></figure>

   The width the owner dragged is on the FIGURE, and the image fills it — so
   without these rules a resized picture would render at its natural size and
   the resize would look like it had not saved. The class names are the
   editor's own and are allow-listed one by one in App\Support\HtmlSanitizer;
   this is the other half of that pair, and the two have to be changed
   together. */
.prose figure { margin: 1.6em 0; }
.prose figure img { margin: 0; }

/* The dragged width is a percentage of the text column, so the image has to
   fill whatever the figure was set to rather than stop at its own pixel size —
   otherwise dragging a small picture wider would do nothing at all. */
.prose figure.image_resized { max-width: 100%; }
.prose figure.image_resized img { width: 100%; }

/* Alignment. Centre is the default because a picture in a 70ch column almost
   always wants to be centred; left and right are `margin` rather than `float`
   so that text never wraps around a picture in a column this narrow, which on
   a phone produces two-word lines down the side of an image. */
.prose figure.image { margin-left: auto; margin-right: auto; }
.prose figure.image-style-block-align-left,
.prose figure.image-style-align-left { margin-left: 0; margin-right: auto; }
.prose figure.image-style-block-align-right,
.prose figure.image-style-align-right { margin-left: auto; margin-right: 0; }
.prose figure.image-style-align-center { margin-left: auto; margin-right: auto; }
.prose figure.image-style-side { margin-left: auto; margin-right: 0; max-width: 50%; }

.prose figure figcaption {
  margin-top: .6em;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
  text-align: center;
}

/* Tables are wrapped in a figure by the editor too. The wrapper must not
   inherit the centring above, or a full-width table would be pushed off
   centre by its own auto margins. */
.prose figure.table { margin-left: 0; margin-right: 0; max-width: 100%; overflow-x: auto; }
.prose figure.table table { margin: 0; }

/* ---------------------------------------------------------- consent tick

   The checkbox under the launch-alert form. It cannot be dropped and it
   cannot arrive pre-ticked — these are parents' addresses in the EU, and the
   agreement has to be something they DID (GDPR art. 4(11)). The server
   enforces the same thing, so this control is load-bearing rather than
   decorative.

   Its own modifier because `.field` is a grid built for a labelled text input:
   inherited, the checkbox would stretch to the full 100% width of `.field
   input` and the label would sit above it, which reads as a broken text box
   rather than as something to tick. */
.field--consent {
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 12px;
  margin-top: 4px;
}

/* It sits inside the two-column form grid, above the button, and has to run
   the full width — a sentence wrapped into half a row is unreadable, and this
   is the one sentence on the page that has to be read. */
.notify-row .field--consent { grid-column: 1 / -1; }

.field--consent input[type="checkbox"] {
  /* Explicit dimensions: the rule above sets width:100% on every .field input,
     and a checkbox is the one control that must not obey it. */
  width: 22px;
  height: 22px;
  flex: none;
  margin: 1px 0 0;
  padding: 0;
  border-radius: 6px;
  accent-color: var(--leaf);
  /* 22px is above the 24px-ish minimum once the label's click area is counted;
     the label is bound with `for`, so the whole sentence is the target. */
  cursor: pointer;
}

.field--consent label {
  /* Not the bold 15px of a field label — this is a sentence to read, not a
     caption to scan past, and bolding it makes the form look like it is
     shouting a legal warning at somebody who just wants to be told about an app. */
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink-soft);
  cursor: pointer;
}

.field--consent em { grid-column: 1 / -1; }

.field--consent.field--bad input[type="checkbox"] { outline: 2px solid #A32020; outline-offset: 2px; }

/* --------------------------------------------------------------------------
   18. Page blocks

   The page builder renders through the classes above — .band, .wrap, .head,
   .card, .fact, .plist, .faq, .shot, .frame, .prose, .story, .final, .stores.
   That is the point of it: a block is not a new drawing, it is one of the
   drawings this page already makes, filled in by the owner.

   What follows is the short list of things a BUILT page can do that the home
   page never had to, and nothing else. Every value here is taken from a rule
   already stated above rather than chosen fresh — the 12-column grid and its
   820px breakpoint are .cards', the gaps are .cards' gap, the band padding is
   .band's clamp() scaled. A second set of styles is how a builder starts
   producing pages that look ALMOST right, so this section is deliberately the
   size it is, and anything added to it should have to argue for itself.
   -------------------------------------------------------------------------- */

/* Columns. The same 12-column grid .cards establishes, so a two-column block
   sits on the same rhythm as a row of cards further down the page.

   Two columns are 7/5, not 6/6. Equal halves read as a table; the uneven split
   is the same judgement .cards makes one section up, and it gives the column
   that holds the words the wider half. Three columns are thirds, because there
   is no honest way to make three uneven columns repeat, and `align-items:
   start` keeps them from stretching to a common height — a row of three
   identical equal-height tiles is the generator's signature. */
.cols {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(18px, 2.6vw, 26px) clamp(22px, 3vw, 34px);
  align-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}
.cols > * { grid-column: span 12; }
@media (min-width: 820px) {
  .cols--2 > *:nth-child(odd)  { grid-column: span 7; }
  .cols--2 > *:nth-child(even) { grid-column: span 5; }
  .cols--3 > * { grid-column: span 4; }
}
.cols h3 { margin: 14px 0 .3em; }
.cols .prose { max-width: none; }   /* the column IS the measure here */
.cols .frame { margin-bottom: 4px; }

/* A single image block. Two widths, both named: the full column, or the same
   62ch the body text is set to — so a picture meant to sit with the prose
   lines up with the prose instead of landing on a number somebody picked. */
.blk-media { margin: 0; }
.blk-media--text { max-width: 62ch; }

/* A deliberate gap. Three sizes off the same clamp() ladder .band uses, so a
   spacer is always a multiple of the rhythm rather than a stray number. */
.band--gap-small  { padding-block: clamp(20px, 3vw, 34px); }
.band--gap-medium { padding-block: clamp(40px, 5.5vw, 68px); }
.band--gap-large  { padding-block: clamp(80px, 11vw, 140px); }

/* The story grid is 180px of mascot plus the rest. Without a mascot the quote
   would be squeezed into the 180px column, so it takes the whole row back. */
@media (min-width: 860px) {
  .story--solo { grid-template-columns: minmax(0, 1fr); }
}

/* An answer that needed two paragraphs. .faq .answer p zeroes the margin so a
   one-paragraph answer sits tight in its card; the second one still needs a
   gap, or the two run together into a wall. */
.faq .answer p + p { margin-top: 1em; }

/* --------------------------------------------------------------------------
   Cookie bar — the consent strip, and the footer control that reopens it
   --------------------------------------------------------------------------

   FIXED, so it overlays the page instead of participating in layout: it can
   appear, disappear and reappear without moving a single line of content
   (CLS 0 by construction). The ink band look ties it to the footer — it is
   furniture of the same species, not a modal demanding tribute.

   The two buttons are the SAME .btn on the SAME ground. Consent law asks
   that refusing be as easy as agreeing; this stylesheet's answer is that
   the two are literally indistinguishable in weight, size and effort. */

.cookie-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 60;               /* over content, under nothing — nothing else floats here */
  background: var(--ink);
  color: var(--paper);
  padding: 14px 0;
  box-shadow: 0 -10px 30px rgba(39, 65, 86, .25);
}

.cookie-bar-inner {
  display: flex;
  flex-wrap: wrap;           /* phone: sentence above, buttons below — no squeezing */
  align-items: center;
  gap: 10px 22px;
}

.cookie-bar-text { margin: 0; font-size: 15px; line-height: 1.45; flex: 1 1 34ch; }
.cookie-bar-text a { color: inherit; text-decoration: underline; }

.cookie-bar-actions { display: flex; gap: 10px; margin: 0; }

/* Smaller than the page's CTAs on purpose — this is an answer, not an offer —
   and shadowless because the candy lift belongs to the paper page, not to an
   ink strip. Both answers, one rule: equal weight is the whole point. */
.cookie-btn {
  padding: 9px 20px;
  font-size: 15px;
  box-shadow: none;
  border-color: var(--paper);
}
.cookie-btn:hover { transform: translateY(-1px); }
.cookie-btn:active { transform: translateY(1px); box-shadow: none; }

/* The footer's reopen control: a button that must read as the fine print it
   sits in — same size, same soft ink, plus the underline that says "this
   does something". On the ink band the inherited .fine colour already fits. */
.cookie-reopen {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}
