/*
 * Nanija, the one stylesheet.
 *
 * It lived inline in templates/layout.php, which meant every page carried
 * all 48 KB of it, and a player sent it again on every click: a few hundred
 * clicks a session, most of them on a phone. Here it is fetched once and
 * kept. layout.php links it with ?v=<file time>, so a deploy is picked up on
 * the next page view and nobody is left with yesterday's styles in a cache.
 *
 * The comments stay. They explain why each rule exists, and 48 KB fetched
 * once is not worth losing that for.
 */
  :root {
    --ink: #1a1f1e; --muted: #5d6b67;
    --line: #d8dedc; --ground: #f4f6f5; --panel: #fff;
    --accent: #1e7a68; --accent-soft: #e2efec; --warn: #a8431f;
    /* Two additions for the chunky shell. --edge is a line dark enough to
       read as a border rather than as a hairline, which is what makes a
       panel look built instead of drawn. --accent-dark is the shadow a
       button casts, and the colour it snaps down onto. */
    --edge: #b4c0bc; --accent-dark: #13513f; --warn-dark: #6d2b13;
    --pixel: 'Pixelify Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  }
  /* The pixel typeface, self-hosted.
     ------------------------------------------------------------------
     Pixelify Sans, because it is the only pixel font I could find that
     covers Lithuanian in full: ą č ę ė į š ų ū ž are all in its
     latin-ext block and lt_Latn is in its language list. Most pixel
     fonts stop at ASCII, and a game whose materials are called ąžuolas
     and juodmedis cannot use one of those.

     Self-hosted rather than linked from Google. This project makes no
     external requests, its players are in the EU, and hotlinking a font
     CDN means every page view tells a third party who is playing.

     The file is optional by design. The fallback is the same system
     stack the body uses, so if public/font is empty the game looks
     exactly as it did before this commit rather than dropping into
     Courier. Nothing breaks, the pixels just do not arrive.

     Scoped deliberately: headings, the bar, buttons, labels and every
     number. NOT body text. This is a reading game — recipes, declined
     material names, the whole "kam tinka" page — and a pixel face over
     three paragraphs of Lithuanian is a costume that gets in the way of
     the thing it is dressing. */
  @font-face {
    font-family: 'Pixelify Sans';
    /* The .ttf only, because it is the file that exists. Listing a .woff2
       first cost a request on every page view that fell through to the PHP
       router and came back as a 404 page, before the browser tried this,
       which is also why the pixels sometimes arrived visibly late. */
    src: url('/font/pixelify-sans.ttf') format('truetype');
    font-weight: 400 700;
    font-display: swap;
    /* Everything except 0-9. With the digits carved out of the face, the
       browser takes them from the next font in the stack, which is the
       system sans, so a number is legible wherever it lands: in a pixel
       heading, in the bar, in a bare "/ 219". See the note on .num below
       for why this is not optional. */
    unicode-range: U+0000-002F, U+003A-10FFFF;
  }

  * { box-sizing: border-box; }
  /* A faint dot grid rather than flat grey: graph paper, or the grid under
     a pixel drawing. Enough to make the white panels stand up off the page,
     too faint to read as a pattern. */
  body {
    background-image: radial-gradient(rgba(19, 81, 63, 0.09) 1px, transparent 1.2px);
    background-size: 8px 8px;
  }
  body {
    margin: 0; background-color: var(--ground); color: var(--ink);
    font: 16px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
  }
  /* Where the pixels go. Everything here is a label, a figure or a
     control: things read at a glance rather than read through. */
  h1, h2, h3,
  .bar, button, .btn,
  .tag,
  .place span, .pick-name, .beast,
  table.ladder th, .medal .who {
    font-family: var(--pixel);
  }
  /* But never the digits.
     Pixelify's 5 and 8 are the same shape at small sizes, and so are 3 and
     8 at a glance. That was found the expensive way: a stamina reading of
     57,38 was read as 87,38, which turned one ordinary action into what
     looked like a thirty-point bug and cost an afternoon of hunting for it.
     A player misreading their stamina or a price is worse than any
     amount of lost charm, so every number in the game is set in the plain
     face, bold where it matters, wherever it sits, including inside the
     pixel headings and the bar. */
  .num, .n {
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-variant-numeric: tabular-nums;
  }
  /* Pixelify sits small and tight next to a system sans, so the parts
     that carry weight get a little back. */
  h1, h2, h3 { letter-spacing: 0.01em; }
  .bar strong, .num { font-variant-numeric: tabular-nums; }

  .wrap { max-width: 900px; margin: 0 auto; padding: 16px; }
  a { color: var(--accent); }
  h1, h2, h3 { line-height: 1.2; margin: 0 0 8px; }
  h1 { font-size: 1.4rem; }
  h2 { font-size: 1.1rem; margin-top: 0; }

  .bar {
    display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline;
    background: var(--panel); border: 2px solid var(--edge);
    border-radius: 0; padding: 10px 14px; margin-bottom: 14px;
  }
  .bar strong { font-size: 1.05rem; }
  .bar .sp { margin-left: auto; }
  /* The navigation, same on every page. Its own flex row so the links keep
     their spacing when the bar wraps on a phone, and so the current page —
     rendered as plain text rather than a link — sits on the same baseline. */
  .bar .nav { display: flex; flex-wrap: wrap; gap: 4px 12px; }
  .bar .nav strong { font-size: inherit; }

  /* The bar is the game's HUD, so it looks like one.
     It was the same white box as every panel under it, which made the one
     strip present on every page the easiest thing on the page to read past.
     Dark green, the colour the buttons already cast as their shadow, so it
     belongs to the palette rather than arriving from outside it; light text;
     the stamina meter in amber, the classic colour of a bar you are meant
     to keep an eye on. */
  .bar {
    background: var(--accent-dark); color: #f2f6f4;
    border-color: var(--accent-dark);
  }
  .bar .muted { color: #b9d3cb; }
  .bar a { color: #d7ece5; }
  .bar a:hover { color: #fff; }
  .bar a.fuel { color: #f2f6f4; }
  .bar a.fuel:hover .meter { outline-color: #f0c040; }
  /* Where you are: plain text already, now a small lit tab so it reads as
     "here" rather than as a link that forgot its underline. */
  .bar .nav strong {
    background: rgba(255, 255, 255, 0.14); padding: 0 6px; color: #fff;
  }
  .bar .meter { background: rgba(255, 255, 255, 0.12); border-color: #3f7a69; }
  .bar .meter i { background: #f0c040; }
  /* The ticks are gaps cut in the colour behind the meter, so on a dark bar
     they have to be dark too or they read as white stripes. */
  .bar .meter::after {
    background: repeating-linear-gradient(90deg, transparent 0 8px, var(--accent-dark) 8px 10px);
  }
  /* Running low turns the fill red, so the moment to eat is visible before
     a run stops half way. */
  .bar .meter.low i { background: #e06a45; }
  /* On a desktop the bar stays on screen while the page scrolls, so the
     stamina and the way to every other page are never more than a glance
     away on a long recipe or the Kolekcija wall. Not on a phone: there
     the bar is three lines tall, and pinning it would take a third of
     the screen from the thing being read. */
  @media (min-width: 760px) {
    .bar {
      position: sticky; top: 0; z-index: 20;
      box-shadow: 0 3px 0 rgba(0, 0, 0, 0.12);
    }
  }
  .bar .full {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
    color: #13513f; background: #f0c040; padding: 0 5px;
  }

  .meter {
    /* display matters: width and height are ignored on an inline element, so a
       meter outside a flex parent renders as nothing at all. It used to be
       flexed by its container and vanished the moment it was not. */
    display: inline-block; vertical-align: middle; position: relative;
    height: 12px; background: var(--ground); border: 2px solid var(--edge);
    border-radius: 0; overflow: hidden; width: 140px;
  }
  .meter i { display: block; height: 100%; background: var(--accent); }
  /* Ticks, drawn over the fill rather than under it, so the bar reads as a
     row of cells that light up one by one. This is the oldest trick in the
     genre and it costs one pseudo-element: a smooth bar says "62.4%", a
     segmented one says "six of ten", and the second is the thing a player
     is actually asking. */
  .meter::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: repeating-linear-gradient(
      90deg, transparent 0 8px, var(--panel) 8px 10px
    );
  }

  /* The stamina readout is one unit: figures and meter together, so the whole
     thing is a target rather than a six-pixel bar. Not `a.fuel` — on the food
     page it is a span, because there it would be a link to the page you are
     already reading, and that is exactly where the meter matters most. */
  .fuel {
    display: inline-flex; align-items: baseline; gap: 8px;
    color: inherit; text-decoration: none;
  }
  a.fuel:hover { color: var(--accent); }
  a.fuel:hover .meter { outline: 1px solid var(--accent); }

  /* Two columns, and the second one is used: the job in hand on the left,
     everything about where you are on the right. A single wide column meant
     scrolling past the location's whole action list to reach the recipe. */
  .cols { display: grid; gap: 14px; align-items: start; }
  @media (min-width: 760px) { .cols { grid-template-columns: minmax(0, 1fr) 240px; } }
  .cols.solo { grid-template-columns: minmax(0, 1fr); }
  /* Two equal columns, for pages where neither side is a sidebar. */
  @media (min-width: 760px) { .cols.even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

  /* Leaderboards. */
  .board-row { align-items: center; }
  .board-row.here { background: var(--accent-soft); }
  .board-who { display: inline-flex; align-items: center; gap: 8px; }
  .rank {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.6em; height: 1.6em; font-size: 0.8rem; color: var(--muted);
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif; font-weight: 700;
  }
  .rank.r1, .rank.r2, .rank.r3 { border-radius: 50%; color: #3a2a00; box-shadow: inset 0 -2px 0 rgba(0,0,0,0.18); }
  .rank.r1 { background: #f0c040; }
  .rank.r2 { background: #cfd6da; color: #2b3336; }
  .rank.r3 { background: #d49a63; color: #3b200a; }

  .panel {
    background: var(--panel); border: 2px solid var(--edge);
    border-radius: 0; padding: 14px; margin-bottom: 14px;
  }
  .panel.tight { padding: 10px 12px; }
  .panel:last-child { margin-bottom: 0; }

  /* A single line of context — a mission, a chain — that should register
     without claiming as much of the page as a titled panel. */
  .strip {
    background: var(--panel); border: 2px solid var(--edge);
    border-left: 4px solid var(--accent);
    border-radius: 0; padding: 8px 12px; margin-bottom: 14px;
    font-size: 0.95rem;
  }
  .strip.back { border-left-color: var(--warn); }
  .strip ol { margin: 4px 0 0; padding-left: 20px; }
  .strip li { margin-bottom: 2px; }

  ul.plain { list-style: none; margin: 0; padding: 0; }
  ul.plain li { padding: 4px 0; border-bottom: 1px solid var(--line); }
  ul.plain li:last-child { border-bottom: 0; }
  /* A plain list of names needs no rules between the rows. */
  ul.plain.bare li { border-bottom: 0; padding: 2px 0; }

  .row { display: flex; justify-content: space-between; gap: 12px; }

  /* What you just gained, as a way out of the grind. Underlined on hover
     rather than always: the report is read at a glance and a line of
     underlines would turn a result into a menu. The dotted rule says
     "there is something here" without shouting it. */
  a.made-link {
    color: inherit; text-decoration: none;
    border-bottom: 1px dotted var(--accent);
  }
  a.made-link:hover { border-bottom-style: solid; background: var(--accent-soft); }

  /* The wealth page, in columns. Even grouped, a full inventory is sixty-odd
     rows, and a single column of them is a page whose bottom half nobody has
     ever seen. Two columns halve the height at desktop width and collapse back
     to one where there is no room; break-inside keeps a category and its
     heading together rather than splitting them across the fold. */
  .hoard { columns: 2; column-gap: 28px; }
  .hoard-group { break-inside: avoid; margin: 0 0 12px; }
  /* One row per kind of thing: the drawing and the word on the left, each
     material and its count wrapping along the right. */
  ul.kinds li.kind {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 5px 0; border-bottom: 1px solid var(--line);
  }
  .kind-name {
    flex: 0 0 9.5em; display: inline-flex; align-items: center; gap: 6px;
    color: var(--ink); text-decoration: none; font-weight: 600;
  }
  .kind-name:hover { color: var(--accent); }
  .kind-list {
    flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: 2px 12px;
    font-size: 0.92rem; padding-top: 2px;
  }
  .variant { white-space: nowrap; }
  .variant .muted { font-size: 0.85rem; }
  @media (max-width: 760px) { .hoard { columns: 1; } }

  /* Requirements as chips rather than a sentence. A recipe is a checklist, and
     a checklist wants to be scanned: shape and colour answer "can I do this"
     before any of the words are read. The words stay, because eleven knives
     differ only by their material and no mark can carry that. */
  .chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 0 0 10px; }
  .chip {
    display: inline-flex; align-items: baseline; gap: 6px;
    border: 1px solid var(--line); border-radius: 0;
    padding: 3px 10px; background: #fff; font-size: 0.9rem;
    color: inherit; text-decoration: none;
  }
  /* The legacy drawings, which are not a uniform size: 15x11 to 48x48, and
     one 75x11 plank. Bounding both axes and letting the browser keep the
     aspect means none of them stretches and none blows out its chip. */
  .ico {
    max-height: 22px; max-width: 30px; width: auto; height: auto;
    align-self: center; image-rendering: crisp-edges;
  }
  /* A picture and its name are one thing: kept together so a wrapping line
     never leaves a drawing stranded from what it is a drawing of. */
  .item { display: inline-flex; align-items: center; gap: 4px; }
  /* Not yet made. Drained rather than hidden, which is what makes a collection
     a collection: you can see the shape of what you have not got. */
  .ico.locked { filter: grayscale(1); opacity: 0.3; }
  .chip .n { font-variant-numeric: tabular-nums; font-weight: 600; }
  .chip .have { color: var(--muted); font-size: 0.85em; }
  .chip .mark { color: var(--accent); }
  /* Held: settled, quiet, green. */
  .chip.ok { border-color: var(--accent); background: var(--accent-soft); }
  /* Short: the only ones that need attention, and the only ones that click. */
  .chip.low { border-color: var(--warn); color: var(--warn); }
  .chip.low .have { color: var(--warn); }
  a.chip.low:hover { background: #f8e6e0; }
  /* Nothing to find: this rung of this recipe does not exist. */
  .chip.out { opacity: 0.5; }
  /* What this job produces. Outlined, never filled: a solid accent pill is
     what a button looks like everywhere else on the page, and this one does
     nothing when you press it. The only chips that may look pressable are the
     ones that are. */
  .chip.made { border-color: var(--accent); color: var(--accent); background: transparent; }
  .chip.made .have, .chip.made .mark { color: var(--accent); }
  /* It is a link now — to "kam tinka", which is what somebody pressing it was
     hoping for all along. Still outlined: the destination is a page, not an
     action, and only buttons get filled. */
  a.chip.made:hover { background: var(--accent-soft); }
  /* What a run used up. Quiet on purpose: it is the price of the result,
     not the result, and it sits after the gains in the report. */
  .chip.spent { color: var(--muted); border-style: dashed; background: transparent; }
  /* On a phone the recipe chips stack one per line, which is right for a
     checklist and wrong for a report: stacked, three gains and two losses
     push the button off the screen, and not having to scroll is the thing
     the old game's players asked for most. So the report keeps its chips
     in a wrapping row, smaller. */
  @media (max-width: 560px) {
    .result .chips { flex-direction: row; align-items: center; }
    .result .chip { padding: 3px 8px; }
  }
  /* The haul: what the button was pressed for, first line of the report
     and the largest chips on the page. Everything under it is smaller. */
  .result .haul-line { margin: 0 0 8px; }
  .result .haul-line .chip {
    font-size: 1.05rem; padding: 5px 12px; border-width: 2px;
    background: var(--accent-soft);
  }
  .result .haul-line .chip .n { font-weight: 700; }
  .result .haul-line .ico { max-height: 28px; max-width: 36px; }
  /* The rest of what changed on the shelves: drops and what was used up. */
  .result .aside .chip { font-size: 0.85rem; }

  /* There was a green variant here for a shortage you could fix immediately.
     It went, because green already means "you have this" on every other chip
     and using it for something you do NOT have was a false claim made in
     colour. Green is held: one meaning, everywhere. */
  /* The arrow that turns a list of ingredients into a recipe.
     It was a grey 16px glyph between two bordered pills — the lightest mark
     on a line of heavy ones, which is backwards: the whole sentence is "these
     BECOME that", and the arrow is the verb. Accent-coloured, half again as
     large and bold, with room either side so it reads as a join rather than
     as punctuation stuck to the chip before it. */
  .chips .to {
    color: var(--accent); font-size: 1.5rem; font-weight: 700;
    line-height: 1; padding: 0 2px;
  }
  .to .narrow { display: none; }
  /* A chip you can tap has to look different from one you cannot. Colour does
     not do it on its own, and there is no hover on a phone to find out with. */
  .chip .go { color: var(--warn); font-weight: 600; }

  /* Phone. Everything above assumes a row has room to be a row; at 375px the
     header is four figures plus six links, and a recipe can be seven pills.
     Nothing is hidden here — hiding is how information ends up existing only
     for people with a mouse — it is all just allowed to go vertical. */
  @media (max-width: 560px) {
    .wrap { padding: 12px; }
    .bar { gap: 6px 14px; }
    /* The nav drops to its own full-width row instead of being pushed to the
       right of figures it then has to wrap around. */
    .bar .nav { flex-basis: 100%; margin-left: 0; gap: 2px 14px; }
    /* Six links two rows deep need vertical room around each one, or the
       wrong page is one mis-aimed thumb away. */
    .bar .nav > * { padding: 4px 0; }
    .meter { width: 110px; }

    /* One requirement per line, each a comfortable target rather than a
       26-pixel pill in a row of seven. */
    .chips { flex-direction: column; align-items: stretch; gap: 4px; }
    .chip { justify-content: flex-start; padding: 8px 12px; border-radius: 0; }
    .chip .go { margin-left: auto; }
    /* The arrow between what goes in and what comes out points down when the
       list is a column. Both glyphs are in the markup because CSS swapping
       content is a trick, and this is a word the page is saying.
       Centred on its own row: stacked chips are full width, so an arrow left
       against the margin looks like it belongs to the chip above rather than
       to the gap between the two halves of the recipe. */
    .chips .to { text-align: center; padding: 2px 0; }
    .to .wide { display: none; }
    .to .narrow { display: inline; }
  }

  /* Medals.
     A list of sixty-five names is a list; a wall of medals is a collection,
     and the difference is entirely in being able to see the whole thing at
     once and spot the gaps. The rim is coloured by the material the first one
     was made from, so a shelf of gold rims is a claim about how you play and
     not just how long. */
  .medals { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); margin: 8px 0 0; }
  .medal {
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 8px 4px; text-align: center; text-decoration: none; color: inherit;
    border-radius: 0;
  }
  .medal .rim {
    display: flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; border-radius: 50%;
    border: 3px solid var(--line); background: #fff;
  }
  .medal .rim img { max-height: 30px; max-width: 34px; width: auto; height: auto; }
  .medal .who { font-size: 0.75rem; line-height: 1.25; }
  .medal .when { font-size: 0.7rem; color: var(--muted); }
  /* Earned, by the material it was first made from. */
  .medal.t1 .rim { border-color: #a9743f; }            /* the common rungs */
  .medal.t2 .rim { border-color: #8b97a1; }            /* the middle */
  .medal.t3 .rim { border-color: #c9a227; }            /* the precious end */
  .medal.t1 .rim, .medal.t2 .rim, .medal.t3 .rim { box-shadow: inset 0 0 0 2px #fff; }
  /* Not yet made: a hole in the wall, and a link to the job that fills it. */
  .medal.locked .rim { border-style: dashed; background: transparent; }
  .medal.locked .rim img { filter: grayscale(1); opacity: 0.3; }
  .medal.locked .who { color: var(--muted); }
  a.medal:hover { background: var(--accent-soft); }
  a.medal:hover .rim { border-color: var(--accent); }

  /* The eight qualities. Rings rather than bars, because 2 Pt 1,5-7 supplies
     each quality in the one before it rather than after it — concentric, not
     sequential. A bar would quietly assert stages the passage does not claim.

     One column on a phone: eight cards side by side is eight unreadable
     cards, and the order matters here, so wrapping them into a grid that
     reflows would scramble the chain. */
  .virtues { display: grid; gap: 12px; margin: 0 0 14px; }
  @media (min-width: 720px) { .virtues { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .virtue { margin: 0; }
  .virtue-head { display: flex; align-items: center; gap: 12px; }
  .ring { flex: 0 0 auto; }
  .ring-track { fill: none; stroke: var(--line); stroke-width: 7; }
  .ring-fill {
    fill: none; stroke: var(--accent); stroke-width: 7; stroke-linecap: round;
  }
  /* Not counted yet: drawn, empty, and plainly inert rather than at zero —
     "we do not measure this" and "you have none of this" are different
     claims and the page should not blur them. */
  .virtue-off { opacity: 0.72; }
  .ring-off .ring-track { stroke-dasharray: 3 5; }

  /* A label, a meter and its numbers on one line. Used for how far through
     the pile you are, and for the stamina bar on the page about stamina. */
  .gauge { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 10px; }
  .gauge .meter { width: 160px; }
  .muted { color: var(--muted); font-size: 0.9rem; }
  .num { font-variant-numeric: tabular-nums; }
  /* Set apart from whatever is above it, which on the job page is the
     material fold. Flush against that summary line, the mechanism paragraph
     sat at the same indent under the disclosure triangle and read as the
     fold's contents — so the page appeared to be showing you something it had
     just told you was collapsed. The gap is the whole fix: a closed fold is
     one line, and what follows is plainly not part of it. */
  .recipe { margin: 12px 0 10px; }
  .recipe .arrow { color: var(--muted); }

  /* Anything long enough to be worth hiding until asked for: the material
     ladder, a location's job list. One line closed, the full thing open. */
  .fold {
    border: 1px solid var(--line); border-radius: 0;
    padding: 6px 10px; margin: 0 0 10px;
  }
  .fold > summary {
    cursor: pointer; list-style-position: outside;
    /* The summary is a whole sentence of small parts; let it wrap on a phone
       rather than push the card sideways. */
    display: list-item; line-height: 1.5;
  }
  .fold > summary:hover { color: var(--accent); }
  .fold[open] > summary { margin-bottom: 6px; border-bottom: 1px solid var(--line); padding-bottom: 6px; }
  .fold > summary .tag { margin-right: 2px; }
  /* Inside a panel it is the panel that carries the border. */
  .panel > .fold { border: 0; padding: 0; margin: 0; }
  .panel > .fold > summary h2 { display: inline; }

  table.ladder { border-collapse: collapse; width: 100%; margin: 0; font-size: 0.92rem; }
  table.ladder th {
    text-align: left; font-size: 0.75rem; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--muted); font-weight: normal;
    padding: 0 8px 2px 0; border-bottom: 1px solid var(--line);
  }
  table.ladder th:not(:first-child), table.ladder td:not(:first-child) { text-align: right; }
  table.ladder td { padding: 2px 8px 2px 0; }
  table.ladder tr.here td { background: var(--accent-soft); }
  table.ladder tr.out td { opacity: 0.55; }

  /* The press.
     A button that sits still under the finger is the single biggest reason
     a web page feels like a form and a game feels like a game. Offset solid
     shadow, and on :active the button moves onto its own shadow and the
     shadow goes: the whole thing drops three pixels, which is what a key
     does. No transition on the way down, because the point is that it is
     instant. */
  button, .btn {
    font: inherit; cursor: pointer; background: var(--accent); color: #fff;
    border: 0; border-radius: 0; padding: 7px 14px;
    box-shadow: 3px 3px 0 var(--accent-dark);
  }
  button.ghost {
    background: var(--panel); color: var(--accent);
    border: 2px solid var(--edge); box-shadow: 3px 3px 0 var(--edge);
  }
  button[disabled] { box-shadow: none; cursor: default; }
  input, select {
    font: inherit; padding: 6px 8px; border: 2px solid var(--edge);
    border-radius: 0; background: #fff; max-width: 100%;
  }
  form.inline { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
  /* The quick counts beside Atlikti: smaller than it, and outlined, so the
     main button is still the obvious one to press. */
  .quick { display: inline-flex; gap: 6px; }
  /* The job list: a picture of the output in front of each job, and a small
     green note on the ones every tool and input is already in hand for. */
  .job { display: flex; align-items: center; gap: 8px; }
  .job a, .job strong { display: inline-flex; align-items: center; gap: 6px; }
  .job .ico { max-height: 20px; max-width: 26px; }
  .can-now { color: var(--accent); font-size: 0.78rem; white-space: nowrap; }

  /* "Tęsti": the one big button on the home page, back to the last job. */
  .carry-on {
    display: flex; align-items: center; gap: 8px; text-decoration: none;
    font-size: 1.05rem; padding: 10px 14px; margin: 0 0 14px;
  }
  .carry-on .go { margin-left: auto; color: inherit; font-weight: 700; }
  .quick button { padding: 5px 9px; box-shadow: 2px 2px 0 var(--edge); }

  .flash { background: var(--accent-soft); border-left: 3px solid var(--accent); padding: 10px 14px; margin-bottom: 14px; }
  .error { background: #f8e6e0; border-left: 3px solid var(--warn); padding: 10px 14px; margin-bottom: 14px; }
  .result { background: #fbfcfb; border: 1px solid var(--line); border-left: 3px solid var(--accent); padding: 12px 14px; margin-bottom: 14px; }

  /* Press feedback, and the reason it has to be an animation.
     Every action is a POST, a redirect and a fresh document, so there is no
     "pressed" state to keep — the page you are looking at afterwards is a new
     page that happens to look almost exactly like the old one. The numbers
     that moved are small and grey and in the same place as before, so a run
     that worked perfectly can read as a button that did nothing.
     A CSS animation replays on every load, which is precisely once per press.
     Two things animate, and both are things that actually changed: */

  /* The report, arriving. A brief wash of accent that fades to its normal
     background, so the eye is pulled to the one block that is new. */
  .result { animation: arrived 600ms ease-out; }
  @keyframes arrived {
    from { background: var(--accent-soft); transform: translateY(-3px); }
    to   { background: #fbfcfb; transform: none; }
  }

  /* The scoreboard.
     Four cells, big figure over small label, arriving one after another.
     The stagger is the point: four numbers appearing together are a table,
     four numbers appearing in sequence are a payout. 70ms apart, so the
     whole run lands in under a third of a second and nobody waits for it.
     Delays by nth-child rather than an inline custom property, because
     four is not enough to be worth putting arithmetic in the markup. */
  .score { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 0 0 2px; }
  .score .cell {
    display: flex; flex-direction: column; align-items: flex-start;
    line-height: 1.1;
    animation: scored 240ms cubic-bezier(.2,1.3,.4,1) backwards;
  }
  .score .cell:nth-child(1) { animation-delay: 0ms; }
  .score .cell:nth-child(2) { animation-delay: 70ms; }
  .score .cell:nth-child(3) { animation-delay: 140ms; }
  .score .cell:nth-child(4) { animation-delay: 210ms; }
  .score .n {
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: 1.5rem; font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .score .k {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--muted);
  }
  @keyframes scored {
    from { transform: translateY(6px) scale(0.86); opacity: 0; }
    to   { transform: none; opacity: 1; }
  }

  /* The stamina meter, refilling to where it now stands. Above everything,
     because that is where the eye starts, and honest: stamina is the one
     figure that moves on literally every action. Only after a run — on an
     ordinary page load it would be decoration claiming to be news. */
  .fuel.acted .meter i { animation: refill 700ms cubic-bezier(.22,.9,.3,1); transform-origin: left; }
  @keyframes refill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  /* The instant half: touch has no hover, so without this the only
     acknowledgement of a tap is the page eventually changing. */
  button:active, .btn:active { transform: translate(3px, 3px); box-shadow: none; }
  button[disabled]:active { transform: none; }

  .result.partial { border-left-color: var(--warn); }
  /* The run report now usually renders inside the job panel, under the button
     that caused it, where a trailing margin is just a gap at the bottom. */
  .result:last-child { margin-bottom: 0; }
  .gain { color: var(--accent); }
  .loss { color: var(--warn); }

  /* The rare things, all wearing one shape.
     A level-up, a first of a kind, a new trade, a combo paid out. They have
     nothing in common except that they do not happen every run, and that is
     exactly the thing worth saying in a single visual grammar: when this
     card appears, something happened that usually does not.
     It arrives after the scoreboard rather than with it. The numbers land
     first, then the news. */
  .event {
    display: flex; align-items: center; gap: 10px;
    background: var(--accent-soft); border: 2px solid var(--accent);
    padding: 8px 12px; margin: 8px 0 0; color: var(--ink);
    animation: scored 300ms cubic-bezier(.2,1.3,.4,1) 300ms backwards;
  }
  /* The loudest one. A first of a kind happens once per thing, ever. */
  .event.loud { box-shadow: 3px 3px 0 var(--accent-dark); }
  .event-art {
    flex: none; min-width: 38px; text-align: center;
    font-size: 1.5rem; line-height: 1; color: var(--accent);
  }
  .event-art img {
    max-height: 36px; max-width: 42px; width: auto; height: auto;
    image-rendering: crisp-edges; vertical-align: middle;
  }
  .event-title { font-family: var(--pixel); font-size: 1.05rem; display: block; }
  .event-note { font-size: 0.9rem; color: var(--muted); }
  .event-note strong { color: var(--ink); }

  /* Kept because .first is still the right name for the one thing left
     using it, and because deleting a class is how a page loses its styling
     six templates away from where you were looking. */
  .first {
    background: var(--accent-soft); border-radius: 0;
    border-left: 4px solid var(--accent);
    padding: 6px 10px; color: var(--ink);
  }
  /* A sighting is a curiosity, not an achievement: enough of a mark that the
     eye finds it, nowhere near as loud as a first of a kind. */
  .wild { border-left: 2px dashed var(--line); padding-left: 8px; }

  /* The bestiary. Wrapping chips rather than a list, because most entries are
     one word and forty-two of them down the page would be a scroll. */
  .beasts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
  .beast {
    border: 1px solid var(--line); border-radius: 0;
    padding: 3px 10px; font-size: 0.9rem; background: var(--panel);
  }
  .beast.unmet { color: var(--muted); border-style: dashed; letter-spacing: 0.1em; }
  .beast img { vertical-align: -4px; margin-right: 4px; }

  /* The drawings are 24x30 pixels from 2006 and look it. Scaled up hard and
     told not to smooth, which is the only way old pixel art survives a
     modern screen: blurred, it just looks like a mistake. */
  .mugshot {
    width: 96px; height: 120px; display: block; margin: 0 auto 6px;
    image-rendering: pixelated;
  }

  /* The monster, breathing.
     The legacy art has two facings per creature and the old game picked one
     at random per render, which made sense on a map where the same monster
     stands in six places at once and none here, where it appears alone. So
     the two frames are stitched side by side and alternated instead: a
     two-frame idle, which is the cheapest animation there is and the one
     that separates a picture of a thing from a thing.
     Two explicit keyframes rather than steps(2), because a step function
     over a percentage background-position is the kind of clever nobody can
     read a year later. */
  .mugshot.walk {
    background-image: var(--frames);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: breathe 1100ms infinite;
  }
  @keyframes breathe {
    0%, 55%     { background-position: 0 0; }
    55.01%, 100% { background-position: 100% 0; }
  }
  .beasts .mugshot { width: 24px; height: 30px; display: inline; margin: 0 4px 0 0; }

  /* The one page in the game that interrupts. Centred and roomy, because it
     is asking for a click that buys nothing but attention. */
  .encounter { text-align: center; padding: 18px 14px; }
  .encounter ul.plain { text-align: left; display: inline-block; }
  /* The place tiles: 15x15 drawings from 2006, doubled and told not to
     smooth. Same treatment as the monsters, for the same reason. */
  .tile {
    width: 30px; height: 30px; vertical-align: -7px;
    image-rendering: pixelated; margin-right: 4px;
  }

  /* The world, as twelve buttons. Each one is a submit, so the whole grid
     is one form and travel is a single press with no JavaScript. */
  .places { display: flex; flex-wrap: wrap; gap: 6px; }
  .place {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    background: var(--panel); color: var(--ink);
    border: 2px solid var(--edge); border-radius: 0;
    padding: 6px 4px; min-width: 76px; font-size: 0.82rem; line-height: 1.2;
    box-shadow: 2px 2px 0 var(--edge);
  }
  .place img { width: 30px; height: 30px; image-rendering: pixelated; }
  .place:hover { border-color: var(--accent); }
  /* Where you are standing is not a destination, so it is disabled rather
     than merely marked: a button that looks pressable and does nothing is
     worse than no button. */
  .place.here {
    border-color: var(--accent); background: var(--accent-soft);
    cursor: default; opacity: 1;
  }
  .place[disabled] { filter: none; }

  /* Pick a thing from your own shelves. Radios with the input hidden, so
     the whole tile is the target and the checked one styles itself through
     the sibling selector rather than through a script. */
  .picks {
    display: flex; flex-wrap: wrap; gap: 6px;
    max-height: 320px; overflow-y: auto;
    padding: 2px; margin-bottom: 10px;
  }
  .pick { cursor: pointer; }
  /* Hidden, not display:none. A removed input is unreachable by keyboard
     and invisible to a screen reader, which would make this a picker only
     a mouse can use. */
  .pick input {
    position: absolute; opacity: 0; width: 0; height: 0;
  }
  .pick-body {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    border: 2px solid var(--edge); border-radius: 0; background: var(--panel);
    padding: 6px 8px; min-width: 92px; max-width: 148px;
    font-size: 0.82rem; line-height: 1.25; text-align: center;
  }
  .pick-body img { max-height: 28px; max-width: 36px; image-rendering: crisp-edges; }
  .pick-name { font-weight: 600; }
  .pick-note { font-size: 0.78rem; }
  .pick:hover .pick-body { border-color: var(--accent); }
  .pick input:checked + .pick-body {
    border-color: var(--accent); background: var(--accent-soft);
    box-shadow: inset 0 0 0 1px var(--accent);
  }
  /* Keyboard users get the same ring the mouse never needed. */
  .pick input:focus-visible + .pick-body { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* The compact picker: one line per thing, grouped by kind, in a box that
     scrolls on its own so sixty items do not push the price field and the
     button off the screen. */
  .picklist {
    max-height: 300px; overflow-y: auto; margin-bottom: 10px;
    border: 2px solid var(--edge); background: var(--panel);
  }
  .picklist-head {
    margin: 0; padding: 6px 10px 2px; position: sticky; top: 0;
    background: var(--panel); border-bottom: 1px solid var(--line);
  }
  .pickrow { display: block; cursor: pointer; }
  .pickrow input { position: absolute; opacity: 0; width: 0; height: 0; }
  .pickrow-body {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 10px; border-bottom: 1px solid var(--line); font-size: 0.92rem;
  }
  .pickrow-pic { flex: none; width: 26px; display: flex; justify-content: center; }
  .pickrow-name { flex: 1 1 auto; min-width: 0; }
  /* flex: none, so a squeezed row wraps the NAME rather than sliding the
     note on top of it, which is what it did in a narrow column. */
  .pickrow-note { flex: none; color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
  .pickrow-qty { flex: none; min-width: 3.5em; text-align: right; color: var(--muted); }
  .pickrow:hover .pickrow-body { background: var(--ground); }
  .pickrow input:checked + .pickrow-body {
    background: var(--accent-soft); box-shadow: inset 4px 0 0 var(--accent);
  }
  .pickrow input:checked + .pickrow-body .pickrow-name { font-weight: 600; }
  .pickrow input:focus-visible + .pickrow-body { outline: 2px solid var(--accent); outline-offset: -2px; }
  /* On a phone the price note drops under the name rather than squeezing
     it, so a long material name still fits on its line. */
  @media (max-width: 560px) {
    .pickrow-body { flex-wrap: wrap; row-gap: 0; }
    .pickrow-note { order: 3; flex-basis: 100%; padding-left: 34px; }
  }

  /* The village. One row per building: the drawing on the left at whatever
     size the legacy artist drew it, the price and the button on the right.
     Not a grid of equal cells, because the buildings are not equal — three
     are 32 pixels and five are 75 by 100 — and forcing them into one box
     would either shrink the good ones or pad the small ones. */
  .yard { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
  .lot {
    display: flex; gap: 12px; align-items: flex-start;
    background: var(--panel); border: 2px solid var(--edge); padding: 12px;
  }
  .lot.done { border-color: var(--accent); background: var(--accent-soft); }
  .lot-pic {
    flex: none; width: 96px; display: flex; flex-direction: column;
    align-items: center; justify-content: flex-end; gap: 6px;
  }
  /* The levelled buildings are 32-pixel drawings and the landmarks 75 by
     100. Doubled and native respectively, which lands them at roughly the
     same height instead of the landmarks towering over a postage stamp. */
  .lot-pic img { width: 64px; height: 64px; image-rendering: pixelated; }
  .lot.landmark .lot-pic img { width: 75px; height: 100px; }
  /* What the next level looks like, small, under the one standing. */
  .next-look { display: flex; flex-direction: column; align-items: center; gap: 1px; }
  .next-look .tag { font-size: 0.65rem; }
  .lot-pic .next-look img { width: 32px; height: 32px; opacity: 0.85; }

  /* Just built: one pulse of accent round the card, so the eye lands on
     the building the player pressed the button for after the redirect. */
  .lot.fresh { animation: freshlot 1400ms ease-out; }
  @keyframes freshlot {
    0%   { box-shadow: 0 0 0 4px var(--accent); }
    100% { box-shadow: 0 0 0 0 transparent; }
  }

  /* The village, standing in a row on one strip of ground.
     Bottom-aligned so the buildings stand on the same line whatever their
     height; the border underneath is the ground. Scrolls sideways inside
     itself on a narrow screen, because a skyline that wraps onto a second
     line stops being a skyline, and the page itself must never scroll
     sideways. */
  .town {
    display: flex; align-items: flex-end; gap: 14px;
    overflow-x: auto; padding: 10px 12px 0; margin: 0 0 14px;
    background: linear-gradient(to bottom, var(--panel) 0 70%, var(--accent-soft) 70% 100%);
    border: 2px solid var(--edge); border-bottom: 6px solid #9a7a4f;
  }
  .house {
    flex: none; display: flex; flex-direction: column; align-items: center;
    gap: 2px; text-decoration: none; color: var(--ink);
    padding-bottom: 4px;
  }
  .house img { width: 48px; height: 48px; image-rendering: pixelated; }
  .house.landmark img { width: 54px; height: 72px; }
  .house-name { font-family: var(--pixel); font-size: 0.72rem; white-space: nowrap; }
  .house-name .num { color: var(--accent); }
  /* Not built yet: the outline of what will stand there. Fainter than the
     ghost on the card, because on the skyline it is scenery, not a prompt. */
  .house.unbuilt img { filter: grayscale(1); opacity: 0.18; }
  .house.unbuilt .house-name { color: var(--muted); opacity: 0.7; }
  /* A locked plot says what opens it, right under its outline. Small and
     muted, because it is a note on the scenery rather than a prompt. */
  .house.locked { cursor: default; }
  .house-key {
    font-size: 0.66rem; line-height: 1.1; color: var(--muted);
    white-space: nowrap; margin-top: -1px;
  }
  .house:hover img { transform: translateY(-2px); }
  /* The one just raised hops once into place. */
  .house.fresh img { animation: hop 700ms cubic-bezier(.2,1.6,.4,1); }
  @keyframes hop {
    0%   { transform: translateY(14px) scale(0.6); opacity: 0; }
    100% { transform: none; opacity: 1; }
  }

  /* The build card holds pictures, not an icon, so its art slot is wider. */
  .event.built { margin: 0 0 12px; }
  .event.built .event-art { display: flex; align-items: flex-end; gap: 6px; min-width: 0; }
  .event.built .event-art img {
    width: 48px; height: 48px; max-width: none; max-height: none;
    object-fit: contain; image-rendering: pixelated;
  }
  .event.built .event-art img.was { opacity: 0.45; width: 32px; height: 32px; }
  .event.built .event-art .then { font-size: 1rem; color: var(--muted); }
  .event.built .event-art img.now { animation: hop 700ms cubic-bezier(.2,1.6,.4,1) 250ms backwards; }

  .ready-now {
    margin: 0 0 12px; padding: 8px 12px;
    border-left: 4px solid var(--accent); background: var(--accent-soft);
  }

  /* One bar for how close the next level is. Same segmented meter as the
     stamina, so it reads as cells lighting up. */
  .progress-line { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
  .progress-line .meter { width: 150px; }

  /* An empty plot shows level one drained of colour: the same trick the
     unearned medals use, and the reason a set feels like a set. */
  .lot-pic img.ghost { filter: grayscale(1); opacity: 0.25; }
  .lot-body { flex: 1 1 auto; min-width: 0; }
  @media (max-width: 560px) {
    .lot { flex-direction: column; align-items: center; text-align: center; }
    .lot-body { text-align: left; width: 100%; }
    .chips { justify-content: flex-start; }
  }

  /* Žemėlapis. One SVG that scales to fit; these are its paints. */
  .map-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
  .map-tab {
    padding: 4px 10px; border: 2px solid var(--edge); background: var(--panel);
    text-decoration: none; font-size: 0.92rem;
  }
  .map-tab.here { border-color: var(--accent); background: var(--accent-soft); color: var(--ink); }
  .map-goal { margin: 0 0 10px; }
  .map-goal select { max-width: 60%; }
  .map-wrap { overflow-x: auto; margin: 0 0 14px; }
  svg.map { display: block; width: 100%; max-width: 100%; height: auto; min-width: 560px; }
  svg.map .map-col { font: 11px system-ui, sans-serif; letter-spacing: 0.08em; fill: var(--muted); }
  svg.map .region > rect { fill: var(--panel); stroke: var(--edge); stroke-width: 2; }
  svg.map .region.marked > rect { stroke: #e06a45; }
  /* Places on the road to a goal are outlined in the goal's green. */
  svg.map.goal .region.marked > rect { stroke: var(--accent); }
  svg.map .region.here > rect { stroke: var(--accent); stroke-width: 3; }
  svg.map .region-name { font-family: var(--pixel); font-size: 14px; fill: var(--ink); }
  svg.map .region-count { font: 12px system-ui, sans-serif; fill: var(--muted); }
  svg.map .region-count.full { fill: var(--accent); font-weight: 700; }
  svg.map image { image-rendering: pixelated; }
  svg.map .good rect { fill: #fff; stroke: var(--line); }
  svg.map .good.not rect { fill: #eef1f0; stroke-dasharray: 3 2; }
  svg.map .good.not image { opacity: 0.3; }
  svg.map .good.dim { opacity: 0.25; }
  svg.map a:hover .good rect { stroke: var(--accent); stroke-width: 2; }
  /* The ring is a rect inside the cell's group, so the cell's own paint
     (white fill, grey dashes) reaches it too unless it is undone here, and
     a white-filled ring drawn last covers the drawing it is meant to
     frame. Selected as specifically as the cell rule so it wins. */
  svg.map .good rect.ring { fill: none; stroke-width: 3; stroke-dasharray: none; }
  svg.map .good rect.ring.ok { stroke: var(--accent); }
  svg.map .good rect.ring.need { stroke: #e06a45; }
  svg.map .leg { fill: none; stroke: #e06a45; stroke-width: 3; stroke-dasharray: 8 5; opacity: 0.9; }
  svg.map .leg.ahead { stroke: var(--accent); }
  svg.map .leg-n { fill: #e06a45; stroke: #fff; stroke-width: 2; }
  /* The goal's numbers match its green road, so the two overlays never
     read as one: orange is where you went, green is where to go. */
  svg.map .visit-n { fill: var(--accent); stroke: #fff; stroke-width: 2; }
  svg.map .leg-t { font: 700 12px system-ui, sans-serif; fill: #fff; }
  ol.journey { margin: 0; padding-left: 22px; }
  ol.journey li { padding: 3px 0; }

  /* You, in a list of other people. The boards already mark the row a
     different way per page; this is the one the village uses. */
  ol.plain li.here, ul.plain li.here { background: var(--accent-soft); }

  .badge { color: var(--accent); }
  .tag { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }

/* The goal being followed: one line under the bar on every game page. */
.goal-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
  background: var(--panel); border: 2px solid var(--accent);
  padding: 6px 12px; margin: 0 0 12px; font-size: 0.92rem;
}
.goal-line .goal-name { display: inline-flex; align-items: center; gap: 6px; color: var(--ink); text-decoration: none; }
.goal-line .meter { width: 120px; }
.goal-line .goal-next { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; }
.goal-line .goal-next.ready { font-weight: 700; }
.goal-line .tile { width: 22px; height: 22px; vertical-align: -5px; }

/* ---- "Kol jūsų nebuvo" and the village news ---------------------------- */
.away { background: var(--panel); border: 2px solid var(--accent); padding: 8px 12px; margin: 0 0 12px; }
.away h3 { margin: 0 0 4px; }
.away-list { margin: 0; padding-left: 18px; }
.away-list li { margin: 2px 0; }
.news { list-style: none; margin: 0; padding: 0; }
.news li { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; padding: 4px 0; border-bottom: 1px dotted var(--line, #ccc); }
.news li:last-child { border-bottom: 0; }
.news .ico { width: 24px; height: 24px; image-rendering: pixelated; object-fit: contain; }
.news .who { font-weight: 700; }
.news .when { margin-left: auto; color: var(--muted, #777); font-size: 0.85rem; }

/* ---- The twist of the day --------------------------------------------- */
.twist-line { margin: 0 0 12px; padding: 6px 12px; background: #fff7d6; border: 2px solid #d9b441; }
.twist-line .tile { width: 22px; height: 22px; vertical-align: -5px; }
.twist-tag { display: inline-block; margin-left: 6px; padding: 2px 6px; background: #fff7d6; border: 1px solid #d9b441; font-size: 0.85rem; white-space: nowrap; }
.place { position: relative; }
.place.lucky { border-color: #d9b441; }
.twist-mark { position: absolute; top: -8px; right: -6px; background: #d9b441; color: #1a1f1e; font-size: 0.7rem; font-weight: 700; padding: 0 4px; }

/* ---- The phone menu ---------------------------------------------------- */
/* Five buttons at the bottom of a phone screen, where the thumb is. Hidden
   on anything wider, where the links in the top bar fit on one line. */
.dock { display: none; }
@media (max-width: 560px) {
  .bar .nav { display: none; }
  body { padding-bottom: 68px; }
  .dock {
    display: grid; grid-template-columns: repeat(5, 1fr);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    background: var(--accent-dark); border-top: 2px solid #0b3328;
    padding-bottom: env(safe-area-inset-bottom);
  }
  .dock > a, .dock-more > summary {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-height: 56px; gap: 2px; color: #d7ece5; text-decoration: none;
    font-size: 0.72rem; cursor: pointer; list-style: none;
  }
  .dock-more > summary::-webkit-details-marker { display: none; }
  .dock .glyph { font-size: 1.35rem; line-height: 1; }
  .dock > a.on, .dock-more > summary.on { color: #fff; background: rgba(255,255,255,0.12); font-weight: 700; }
  .dock-more { position: relative; }
  .dock-menu {
    position: absolute; right: 4px; bottom: calc(100% + 6px);
    display: flex; flex-direction: column; min-width: 170px;
    background: var(--panel); border: 2px solid var(--accent-dark);
    box-shadow: 3px 3px 0 var(--accent-dark);
  }
  .dock-menu a { padding: 12px 14px; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line); }
  .dock-menu a:last-child { border-bottom: 0; }
  .dock-menu a.on { font-weight: 700; background: var(--accent-soft); }
}

/* ---- Tool wear ---------------------------------------------------------- */
.wear.soon { color: var(--warn); font-weight: 700; }
.wear-note { margin: 6px 0 0; padding: 4px 8px; border-left: 3px solid var(--warn); background: #fdf1ec; font-size: 0.9rem; }

/* ---- Village work and orders -------------------------------------------- */
.work-list li, .order-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 4px 0; }
.work-list .ico, .order-list .ico { width: 24px; height: 24px; image-rendering: pixelated; object-fit: contain; }
.order-list li { border-bottom: 1px dotted var(--line); }
.order-list li:last-child { border-bottom: 0; }
.order-what { flex: 1 1 220px; }
.order-pay { color: var(--accent-dark); font-size: 0.9rem; }
.order-act { margin-left: auto; }
.order.done { opacity: 0.6; }
.order.ready .order-what strong { color: var(--accent); }
.warn-text { color: var(--warn); }

.village-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 12px; padding: 6px 12px; background: var(--accent-soft); border: 2px solid var(--accent); color: var(--ink); text-decoration: none; }
.village-line .go { margin-left: auto; color: var(--accent); }

.dust-list { margin: 6px 0 0; }
.dust-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
.dust-list label { display: flex; align-items: center; gap: 8px; padding: 5px 0; cursor: pointer; }
.dust-list .dust-use { margin-left: auto; font-size: 0.88rem; }
/* Big enough to see and to hit with a thumb. The general input rule gives
   every input padding and a border, which a checkbox does not need. */
.dust-list input[type="checkbox"] { width: 20px; height: 20px; padding: 0; margin: 0; flex: none; accent-color: var(--accent); }
.dust-list .ico { width: 22px; height: 22px; image-rendering: pixelated; object-fit: contain; }

/* ---- Titles --------------------------------------------------------------- */
.title-tag { display: inline-block; margin-left: 4px; padding: 0 6px; font-size: 0.75rem; font-style: italic; font-weight: 400; color: var(--accent-dark); background: var(--accent-soft); border: 1px solid var(--accent); vertical-align: 1px; }
.title-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; padding: 5px 0; border-bottom: 1px dotted var(--line); }
.title-list li:last-child { border-bottom: 0; }
.title-list .title-name { font-weight: 700; min-width: 160px; }
.title-list li.earned .title-name { color: var(--accent); }
.title-list .meter { width: 90px; }

.excuse { margin: 0 0 6px; font-size: 0.9rem; color: var(--muted); }
.twist-line em { font-style: italic; }

.yield-preview { display: block; flex-basis: 100%; font-size: 0.88rem; margin-top: 2px; }
.nowrap { white-space: nowrap; }

.left-out li { padding: 3px 0; }

.crow-pic { width: 24px; height: 24px; image-rendering: pixelated; vertical-align: -6px; margin-right: 2px; }
.wild.crow.loss { color: var(--warn); }

blockquote.treasure { margin: 6px 0 0; padding: 6px 12px; border-left: 3px solid var(--accent); background: var(--accent-soft); font-style: italic; }
blockquote.treasure .tag { display: block; font-style: normal; margin-top: 4px; }
.verse { font-size: 0.8rem; margin-left: 4px; }

.purse { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 12px; padding: 8px 14px; background: var(--panel); border: 2px solid var(--accent); font-size: 1.05rem; }
.purse strong { font-size: 1.5rem; color: var(--accent-dark); }
.purse .ico { width: 28px; height: 28px; image-rendering: pixelated; }

/* ---- The level-up splash ------------------------------------------------- */
.splash-off:checked + .splash { display: none; }
.splash {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  animation: splash-in 0.35s ease-out both;
}
.splash-backdrop { position: absolute; inset: 0; background: rgba(10, 30, 24, 0.82); cursor: pointer; }
.splash-rays {
  position: absolute; width: 140vmax; height: 140vmax; pointer-events: none;
  background: repeating-conic-gradient(from 0deg, rgba(255, 214, 90, 0.20) 0deg 10deg, transparent 10deg 20deg);
  animation: splash-spin 24s linear infinite;
}
.splash-card {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 26px 40px 22px; min-width: 240px; max-width: calc(100vw - 32px);
  background: var(--panel); border: 4px solid #d9b441; box-shadow: 6px 6px 0 #13513f;
  text-align: center; animation: splash-pop 0.6s cubic-bezier(.2, 1.6, .4, 1) 0.1s both;
}
.splash-kicker { font-family: 'Pixelify Sans', system-ui, sans-serif; font-size: 1.3rem; color: var(--accent-dark); letter-spacing: 0.04em; }
.splash-level {
  font-family: 'Pixelify Sans', system-ui, sans-serif; font-weight: 700;
  font-size: clamp(5rem, 22vw, 9rem); line-height: 1; color: #d9a521;
  text-shadow: 4px 4px 0 #13513f;
}
.splash-note { font-size: 1.05rem; }
.splash-next { color: var(--muted); font-size: 0.9rem; }
.splash-opened { font-size: 1rem; }
.splash-gifts { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 4px 10px; }
.splash-gifts .gift { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; }
.splash-gifts .ico { width: 24px; height: 24px; image-rendering: pixelated; }
.splash-go { margin-top: 10px; cursor: pointer; }
.splash-confetti { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.splash-confetti i {
  position: absolute; top: -20px; left: calc(4% + var(--i) * 7%);
  width: 10px; height: 14px; background: hsl(calc(var(--i) * 47), 80%, 55%);
  animation: splash-fall calc(1.6s + var(--i) * 0.12s) ease-in calc(var(--i) * 0.07s) both;
}
@keyframes splash-in   { from { opacity: 0; } to { opacity: 1; } }
@keyframes splash-pop  { from { transform: scale(0.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes splash-spin { to { transform: rotate(360deg); } }
@keyframes splash-fall {
  0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(105vh) rotate(540deg); opacity: 0.2; }
}

/* ---- Locked by level ------------------------------------------------------ */
.place.locked { opacity: 0.55; filter: grayscale(1); cursor: not-allowed; }
.place.locked img { opacity: 0.6; }
.lock-mark { font-size: 0.72rem; color: var(--muted); }
svg.map .region.locked { opacity: 0.45; filter: grayscale(1); }
.nav .locked, .dock .locked { opacity: 0.45; cursor: not-allowed; text-decoration: none; }
@media (max-width: 560px) {
  .dock > span.locked { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 56px; gap: 2px; color: #d7ece5; font-size: 0.72rem; }
  .dock-menu span.locked { padding: 12px 14px; border-bottom: 1px solid var(--line); color: var(--muted); }
}
.lock-banner { margin: 6px 0; padding: 8px 12px; border: 2px dashed var(--edge); background: var(--ground); }

/* ---- Pagalba ---------------------------------------------------------------- */
.help-toc { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.help h2 { margin-top: 0; }
.help p, .help li { line-height: 1.5; }
.help-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.help-table th, .help-table td { text-align: left; padding: 4px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.help-table tr.here td { background: var(--accent-soft); font-weight: 700; }

/* ---- Learning hints ----------------------------------------------------- */
.hint { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 0 0 12px; padding: 8px 12px; background: #fffbe8; border: 2px solid #e5cf7a; font-size: 0.92rem; }
.hint-bulb { font-size: 1.2rem; }
.hint-text { flex: 1 1 260px; }
.hint-ok { margin-left: auto; }
.hint-ok button { padding: 4px 10px; font-size: 0.85rem; }

/* ---- Construction ---------------------------------------------------------- */
.construction { display: flex; flex-direction: column; gap: 4px; margin: 4px 0 0; padding: 8px 10px; border: 2px dashed #d9b441; background: #fffbe8; }
.construction .meter { width: 100%; max-width: 280px; }
.house.busy img { opacity: 0.6; outline: 2px dashed #d9b441; outline-offset: 2px; }

.landmark-gift { margin: 0 0 8px; padding: 4px 8px; font-size: 0.9rem; border-left: 3px solid #d9b441; background: #fffbe8; }
.landmark-gift.on { border-left-color: var(--accent); background: var(--accent-soft); }

/* ---- The puzzling note ---------------------------------------------------- */
.note-off:checked + .secret-note { display: none; }
.secret-note {
  position: fixed; right: 16px; bottom: 16px; z-index: 60; width: min(360px, calc(100vw - 32px));
  padding: 10px 14px 8px; background: #fdf6e3; border: 2px solid #b58a3a; box-shadow: 4px 4px 0 #6d5320;
  font-size: 0.95rem; animation: note-in 0.5s ease-out both;
}
.secret-note p { margin: 4px 0; line-height: 1.4; }
.secret-kicker { display: block; font-weight: 700; margin-bottom: 2px; padding-right: 18px; }
.secret-close { position: absolute; top: 2px; right: 8px; font-size: 1.3rem; cursor: pointer; color: var(--muted); }
@media (max-width: 560px) { .secret-note { bottom: 76px; } }
@keyframes note-in { from { transform: translateY(20px); opacity: 0; } to { transform: none; opacity: 1; } }
.secret-form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }

.chest { display: flex; align-items: center; gap: 14px; margin: 0 0 12px; padding: 12px 16px; background: #fffbe8; border: 3px solid #d9b441; box-shadow: 4px 4px 0 #13513f; animation: splash-pop 0.5s cubic-bezier(.2, 1.6, .4, 1) both; }
.chest-art { font-size: 2.6rem; line-height: 1; }
.chest-body { display: flex; flex-direction: column; gap: 4px; }
.chest-title { font-family: 'Pixelify Sans', system-ui, sans-serif; font-size: 1.3rem; color: var(--accent-dark); }
.chest-items { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.chest-items .gift { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; }
.chest-items .ico { width: 24px; height: 24px; image-rendering: pixelated; }
.chest-jackpot { border-color: #c0392b; }

  /* Motion is a nicety; for anyone who has asked for less of it, the layout
     is unchanged and nothing is lost but the reveal.
     LAST in the sheet, on purpose. A media query adds no specificity, so a
     rule that switches animation off only wins against the rules written
     before it. This block used to sit in the middle, and every animation
     added below it since (the event cards, the breathing monster, the
     village) quietly ignored the setting. */
  @media (prefers-reduced-motion: reduce) {
    .result, .fuel.acted .meter i, .score .cell, .event,
    .lot.fresh, .house.fresh img, .event.built .event-art img.now { animation: none; }
    .house:hover img { transform: none; }
    /* The idle stops on its first frame, which is the pose the old game
       drew for standing still. */
    .mugshot.walk { animation: none; background-position: 0 0; }
    button:active, .btn:active { transform: none; box-shadow: none; }
    .splash, .splash-card, .splash-rays, .splash-confetti i, .secret-note, .chest { animation: none; }
    .splash-confetti { display: none; }
  }
