/* A layout for a phone: everything fits on screen, nothing scrolls.
   The page is always exactly as tall as the visible part of the window (dvh accounts for the
   browser bar), and inside it are three bands: header, the content that stretches, and the
   actions at the bottom — where the thumb is. */
:root{
  color-scheme:dark;
  --bg:#140f26; --panel:#1e1738; --panel2:#171130; --line:#3a2d5e;
  --ink:#efe7ff; --muted:#a596c9; --accent:#ffb648; --bright:#ffe0a8; --danger:#ff6b5e;
  --good:#7fd88f;
  --cream:#f6ecd8; --cream-ink:#3b2a12;   /* explanation cards — legible at a distance */
  --go:#8cc63f; --go2:#6ea82c;           /* the button that moves you on */
  --pad:14px; --radius:18px;
  --tap:52px;                       /* the smallest finger target */
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;background:var(--bg);color:var(--ink);overscroll-behavior:none}
/* ONE FACE FOR THE WHOLE GAME.
 *
 * This was Georgia with a serif fallback, and the roll card was the one place in monospace —
 * because the tokens say monospace is for numbers. Read side by side, the family preferred the
 * numbers' face for everything, and the reason is real: even widths make digits line up, and
 * 0/O and 1/l stop being guesses. The cost is that a long paragraph in a monospaced face reads
 * a little slower, and that is the trade being taken deliberately.
 *
 * THE STACK HAS TO NAME A WINDOWS FACE. `ui-monospace` resolves to SF Mono on Apple and to
 * nothing at all elsewhere — Consolas and Cascadia Mono are the two every Windows since 7 has,
 * and Liberation Mono covers Linux. Without them a Windows family got whatever `monospace`
 * happens to be, which is usually Courier New and looks like a typewriter receipt.
 */
body{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;overflow:hidden}
button,input,textarea{font-family:inherit}
button{cursor:pointer}

#app{height:100dvh;display:flex;flex-direction:column;max-width:900px;margin:0 auto;position:relative}

/* ---- header ---- */
/* THREE GROUPS, AND THE MIDDLE ONE IS NOT A TITLE ANY MORE. Left is you, centre is what the
   evening is counting, right is the record. The two sides are equal and fixed so the middle is
   actually centred — with `flex:1` on the sides it drifted whenever one of them lost a button. */
.top{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:10px var(--pad);min-height:56px}
.topside{flex:0 0 auto;display:flex;align-items:center;gap:8px}
.topside.right{margin-left:auto}
.top h1{margin:0;font-size:17px;letter-spacing:2px;color:var(--bright);font-weight:normal;
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.top h1[hidden]{display:none}

/* ==== WHAT EXPLORING EARNED — the header's middle ====
   Stars and keys as round tokens, centred between the two fixed sides. Gold is a star, the
   lighter key is a key; a used key is dimmed rather than removed, so the row says what it did. */
.earned{flex:1;min-width:0;display:flex;justify-content:center;align-items:center;gap:6px}
.earned[hidden],.earned.briefing{display:none}
.earned .tok{flex:0 0 auto;width:34px;height:34px;border-radius:50%;border:1px solid var(--line);
  background:var(--panel);display:grid;place-items:center;padding:0;cursor:pointer}
.earned .tok svg{width:20px;height:20px;display:block}
/* ONE KEY WITH THE NUMBER FOUND IN FRONT OF IT — a pill, because a number and a key are read as one
   thing. At 0 it is faded: keys exist before the first one is found. */
.earned .tok.count{display:flex;align-items:center;justify-content:center;width:auto;min-width:54px;height:34px;padding:0 10px 0 12px;border-radius:17px;gap:6px;
  border-color:var(--accent);background:radial-gradient(circle at 30% 50%,rgba(232,165,74,.22),var(--panel) 75%)}
.earned .tok.count .n{font:700 15px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--bright)}
.earned .tok.count.none{border-color:var(--line);background:var(--panel);opacity:.55}
/* THIS ROOM'S KEYS, as dots hanging just under the pill: filled for found, hollow for still hidden. */
.earned .tok.count{position:relative}
.earned .tok.count .dots{position:absolute;left:50%;bottom:-9px;transform:translateX(-50%);
  display:flex;gap:4px;pointer-events:none}
.earned .tok.count .dots i{width:6px;height:6px;border-radius:50%;border:1.5px solid var(--accent);
  background:var(--bg)}
.earned .tok.count .dots i.on{background:var(--accent)}
.earnlist .none.here{color:var(--bright)}
.earned .tok.used{opacity:.4}
.earned .more{font:13px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--muted);
  background:none;border:0;min-width:28px;min-height:34px;cursor:pointer;padding:0 2px}
.earnlist{display:flex;flex-direction:column;gap:12px}
.earnlist .row{display:flex;gap:12px;align-items:flex-start}
.earnlist .row svg{flex:0 0 26px;width:26px;height:26px}
.earnlist .row.used{opacity:.5}
/* The key's name in the same face as every line around it — a serif name over monospace lines read
   as two different screens stuck together. Brighter and heavier is enough to make it the name. */
.earnlist b{color:var(--bright);font-weight:700;font-size:clamp(14px,3.8vw,16px)}
.earnlist .sub{color:var(--muted);font-size:clamp(13px,3.6vw,15px);margin-top:3px}
.earnlist .none{color:var(--muted);font-size:clamp(13px,3.6vw,15px)}

/* ==== SOMETHING FOUND GOES SOMEWHERE ====
   A small mark leaves the story and flies to the drawer it has just landed in. It is the one bit
   of motion in the game that CARRIES INFORMATION rather than atmosphere: it says which of the
   three doors up there is worth opening now, which no sentence does as quickly. */
.fly{position:fixed;z-index:60;pointer-events:none;font-size:24px;line-height:1;
  transition:transform .65s cubic-bezier(.35,.85,.4,1),opacity .65s ease-in}
/* „REDUCE MOTION" ZNAČI NE POMERAJ, NE „NE PRIKAZUJ" — isto pravilo kao na platnu ploče. Ovaj
   znak NOSI PODATAK (koja se fioka u zaglavlju promenila), pa se sa tim prekidačem ne gasi nego
   se pojavi NA cilju i utrne; put je taj koji se ne prelazi. Mesto postavlja `flyTo`. */
@media (prefers-reduced-motion:reduce){ .fly{transition:opacity .4s ease-in} }
/* 12px with letter-spacing is the floor for a label in caps; this one is two or three words. */
.tracklabel{font:12px/1.1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.06em;
  color:var(--muted);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.top .icon{flex:0 0 auto;width:44px;height:44px;border-radius:14px;border:1px solid var(--line);
  background:var(--panel);color:var(--bright);font-size:20px;display:grid;place-items:center;padding:0}
.top .icon:active{background:var(--panel2)}
.top .icon[hidden]{display:none}   /* display:grid would otherwise beat the hidden attribute */
.top .icon .badge{position:absolute;margin:-26px 0 0 22px;background:var(--accent);color:#2a1a08;
  font-family:ui-monospace,monospace;font-size:13px;border-radius:999px;padding:1px 5px}
/* The hero icon is the head cropped out of the full figure (by agreement the head sits in the
   top third of the frame). overflow:hidden here is NOT decoration: without it a 1024px image
   spills out of the button and floods the whole page — which is exactly what happened. */
.top .icon.face-crop{position:relative;overflow:hidden}
.top .icon.face-crop img{position:absolute;width:400%;height:auto;left:-150%;top:-64%;
  max-width:none;opacity:0;transition:opacity .5s}
.top .icon.face-crop img.shown{opacity:1}

/* ---- the middle that stretches ---- */
/* `position:relative` so the look panel can cover the whole reading area — the picture and the
   story under it — and stop at the field somebody writes in. */
.stretch{flex:1 1 auto;min-height:0;position:relative;display:flex;flex-direction:column;gap:10px;padding:0 var(--pad)}

/* ---- the actions at the bottom ---- */
.actions{flex:0 0 auto;display:flex;flex-direction:column;gap:8px;padding:10px var(--pad)
  calc(10px + env(safe-area-inset-bottom))}
.act{display:flex;align-items:center;gap:10px;min-height:var(--tap);width:100%;text-align:left;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  color:var(--ink);font-size:16px;line-height:1.3;padding:10px 14px}
.act:active{background:var(--panel2);border-color:var(--accent)}
.act .dc{margin-left:auto;flex:0 0 auto;font-family:ui-monospace,monospace;font-size:13px;
  color:var(--accent);border:1px solid var(--line);border-radius:999px;padding:3px 9px;white-space:nowrap}
.act.primary{background:linear-gradient(180deg,#4a3416,#3a2a18);border-color:var(--accent);
  color:var(--bright);justify-content:center;text-align:center;font-size:17px;letter-spacing:1px}
/* THE WAY PAST A GOLD QUESTION — "I just go in and look around" — is the size of the answers and
   the colour of an ordinary button: it is not one of the answers, and in gold it read as one. */
.act.primary.quiet{background:var(--panel);border-color:var(--line);color:var(--ink)}
.act.primary.quiet:active{background:var(--panel2);border-color:var(--accent)}
.act.ghost{background:transparent;color:var(--muted);justify-content:center;min-height:44px;font-size:14px}

/* ---- the field for your own action ---- */
/* Typing your own action is the MAIN way to play: that is why the field is wide, several rows
   tall, and comes first. The offered actions sit behind a button below it. */
.ask{display:flex;gap:8px;align-items:stretch}
.ask[hidden]{display:none}      /* display:flex would otherwise beat the hidden attribute */
.ask textarea{flex:1;min-width:0;resize:none;background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius);color:var(--ink);font-size:16px;line-height:1.4;padding:10px 14px}
.ask textarea:focus{outline:none;border-color:var(--accent)}
.ask textarea::placeholder{color:var(--muted);font-style:italic}
.ask .send{flex:0 0 auto;width:56px;border-radius:var(--radius);
  border:1px solid var(--accent);background:#3a2a18;color:var(--bright);font-size:20px}
.ask .send:active{background:#4a3416}

/* ---- the scene picture ---- */
.plate{position:relative;flex:0 0 auto;border-radius:var(--radius);overflow:hidden;background:var(--panel2);
  border:1px solid var(--line)}
.plate img,.plate canvas{display:block;width:100%;height:100%;object-fit:cover}
.plate img{position:absolute;inset:0;opacity:0;transition:opacity .7s ease}
.plate img.shown{opacity:1}

/* THE DRIFT BELONGS TO THE PICTURE, in a wrapper of its own, so the plate's furniture — the name,
   the lamp, the "i" — stays where it is put. */
.plate .plateimgs{position:absolute;inset:0}
.plate .plateimgs.breathe{animation:plate-breathe 40s ease-in-out infinite}
@media(prefers-reduced-motion:reduce){.plate .plateimgs.breathe{animation:none}}

/* THE NAME OF THE PLACE, over the top of its own picture. A dark band under it because a title
   over paint is only legible by luck; the band is the darkest at the top edge and fades out, so
   it reads as light leaving the picture rather than a bar laid on it. */
/* The lamp and the "i" live in the corners of this same plate, so the name keeps its distance
   from both — and symmetrically, or a centred title is not centred. */
/* THE PLATE FLINCHES. A critical failure is the one moment an evening has real weight in it, and
   the picture used to sit through it unmoved. Short, small, and over — anything longer reads as a
   broken layout rather than a bad roll. */
@keyframes plate-knock{
  0%,100%{transform:translate3d(0,0,0)}
  15%{transform:translate3d(-4px,2px,0)}
  35%{transform:translate3d(3px,-2px,0)}
  55%{transform:translate3d(-2px,1px,0)}
  75%{transform:translate3d(1px,0,0)}
}
.plate.knock{animation:plate-knock .6s ease-out}
@media(prefers-reduced-motion:reduce){.plate.knock{animation:none}}

/* THE LIVE LAYER over the plate: the breathing light, the dust, and the ring on a thing the
   evening is pointing at. It is decoration and nothing more, so it never takes a touch. */
.plate .platefx{position:absolute;inset:0;z-index:2;pointer-events:none}

/* AND THE PICTURE BREATHES IN ITS OWN FRAME. Under 2% over forty seconds, and no sideways drift:
   the plate is ALREADY cropped by `object-fit:cover`, so anything more recomposes the picture —
   the first attempt at 4.5% with a translate quietly cut the window out of the control chamber. */
@keyframes plate-breathe{
  0%,100%{transform:scale(1.004)}
  50%{transform:scale(1.020)}
}
.plate img.breathe.shown{animation:plate-breathe 40s ease-in-out infinite}
@media(prefers-reduced-motion:reduce){.plate img.breathe.shown{animation:none}}

.plate .placename{position:absolute;top:0;left:0;right:0;z-index:3;pointer-events:none;
  padding:9px 54px 16px;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;letter-spacing:.06em;text-transform:uppercase;
  color:var(--bright);text-align:center;white-space:nowrap;overflow:hidden;
  font-size:var(--placename-size,clamp(14px,4vw,19px));line-height:1.15;
  text-shadow:0 1px 6px rgba(0,0,0,.85);
  background:linear-gradient(180deg,rgba(0,0,0,.62),rgba(0,0,0,.34) 55%,transparent)}
/* BOLD: it is the one label left on the picture, and it has to read as the title of the place. */
.plate .placename{font-weight:700}
.plate .placename:empty{display:none}
/* A NAME IS NEVER CUT. One line is the aim, and the type shrinks to reach it — but there is a
   floor, below which a title stops being a title, and a name longer than that gets a second line
   at a readable size instead of a first line nobody can read. */
.plate .placename.wrap{white-space:normal}
/* ==== TWO ANSWERS, TWO PICTURES ====
   An open question with two jobs is the picture cut corner to corner, top right to bottom left: the
   first answer's close-up in the upper left, the second's in the lower right, a gold cut between
   them and a number in each that is the number on its button. Each picture is drawn in a landscape
   frame with its subject in the middle, so inside its triangle it is enlarged a little and moved
   toward the triangle's middle — the subject stays whole instead of being cut in half. Under the
   strips of people and things, which stay where they are. */
.plate .split{position:absolute;inset:0;z-index:2;border-radius:inherit;overflow:hidden;pointer-events:none;
  background:var(--bg)}
.plate .split[hidden]{display:none}
.plate .split .half{position:absolute;inset:0;transition:filter .2s ease}
.plate .split .half.a{clip-path:polygon(0 0,100% 0,0 100%)}
.plate .split .half.b{clip-path:polygon(100% 0,100% 100%,0 100%)}
.plate .split .half img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:1}
/* MOVED, BARELY ENLARGED. The close-ups are wide and the plate is tall (a phone, the left page of the
   book), so `cover` already overflows left and right: sliding the picture sideways (`object-position`)
   costs nothing. Up and down it does not overflow, so that shift is small and the enlargement only
   what keeps the triangle covered — 1.35 turned the wheel into two spokes and the beam into a chain. */
.plate .split .half.a img{object-position:72% 50%;transform:translateY(-7%) scale(1.12)}
.plate .split .half.b img{object-position:28% 50%;transform:translateY(7%) scale(1.12)}
.plate .split .seam{position:absolute;inset:0;width:100%;height:100%}
.plate .split .seam line{vector-effect:non-scaling-stroke;fill:none}
.plate .split .seam .shade{stroke:rgba(0,0,0,.75);stroke-width:9}
.plate .split .seam .gold{stroke:var(--accent);stroke-width:2.5}
.plate .split .num{position:absolute;width:34px;height:34px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;font:700 17px/1 Georgia,serif;color:var(--bright);background:rgba(11,7,22,.85);
  border:2px solid var(--accent);box-shadow:0 2px 8px rgba(0,0,0,.6);transform:translate(-50%,-50%)}
.plate .split .num.a{left:24%;top:24%}
.plate .split .num.b{left:76%;top:76%}
/* AND THE ONE BEING POINTED AT IS THE ONE IN THE LIGHT — the button under a finger or the mouse
   lights its half, and the other half steps back. */
.plate .split.lit-a .half.b,.plate .split.lit-b .half.a{filter:brightness(.42) saturate(.55)}
.plate .split.lit-a .num.b,.plate .split.lit-b .num.a{opacity:.45}

/* ==== THE MOMENT ON THE PICTURE ====
   A finished job is STAMPED — a gold seal drops onto the picture with a sweep of light across it,
   and stays under the room's name afterwards. A key or a star APPEARS where the eye already is,
   catches the light, and then flies up to its place in the header. Never in the way of reading:
   it sits on the picture, above the words, and takes a second and a half. */
.plate .moment{position:absolute;inset:0;z-index:11;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;pointer-events:none;overflow:hidden;
  /* DARK ENOUGH FOR THE MARK TO BE THE ONLY THING ON THE PICTURE — a family asked for the seal and
     the key to stand out, and for the words over the picture to go while they do (below). */
  border-radius:inherit;background:rgba(8,5,18,.84);
  transition:opacity .35s ease-in}
/* WHILE A MOMENT IS UP, NOTHING ELSE ON THE PICTURE SPEAKS: the room's name, what was chosen, the
   people and the things along the edges step out and come back with it. */
.plate:has(.moment:not([hidden])) :is(.placename,.chose,#castStrip,#propStrip,#recapBtn,#waysBtn){visibility:hidden}
.plate .moment[hidden]{display:none}
/* ON THE TAP THE WORDS AND THE DARK GO FIRST, AND THE MARK FLIES. The whole box used to fade in a
   third of a second with the mark inside it, so the flight was never seen: the icon twitched toward
   the bag and everything vanished — "something blinks and is gone". */
.plate .moment{transition:opacity .35s ease-in,background-color .3s ease-in}
.plate .moment.out{background-color:rgba(8,5,18,0)}
.plate .moment.out :is(.m-label,.m-sum,.m-sweep){opacity:0;transition:opacity .2s ease-in;animation:none}
.plate .moment .m-sweep{position:absolute;inset:-10% -60%;transform:translateX(-100%);
  background:linear-gradient(100deg,transparent 38%,rgba(255,217,168,.42) 50%,transparent 62%)}
.plate .moment.go .m-sweep{animation:m-sweep 1.15s ease-out forwards}
.plate .moment.key .m-sweep,.plate .moment.star .m-sweep,.plate .moment.place .m-sweep{opacity:.6}
@keyframes m-sweep{to{transform:translateX(100%)}}
.plate .moment .m-icon{width:clamp(104px,30vw,148px);height:clamp(104px,30vw,148px);
  filter:drop-shadow(0 0 18px rgba(232,165,74,.55));will-change:transform,opacity}
.plate .moment .m-icon svg{width:100%;height:100%;display:block}
/* A THING IS SHOWN AS ITSELF — its own drawing in a framed square, the shape it lands as. */
.plate .moment .m-icon.pic{border-radius:14px;overflow:hidden;border:2px solid var(--bright);background:var(--panel2)}
.plate .moment .m-icon.pic img{width:100%;height:100%;object-fit:cover;display:block;position:static;opacity:1}
.plate .moment.noticed.go .m-icon,.plate .moment.carried.go .m-icon,.plate .moment.concluded.go .m-icon{animation:m-pop .5s cubic-bezier(.2,1.4,.45,1) both}
.plate .moment.noticed .m-sweep,.plate .moment.carried .m-sweep,.plate .moment.concluded .m-sweep{opacity:.6}
.plate .moment.seal.go .m-icon{animation:m-stamp .6s cubic-bezier(.2,1.5,.45,1) both}
@keyframes m-stamp{0%{transform:scale(2) rotate(-14deg);opacity:0}55%{opacity:1}100%{transform:scale(1) rotate(-6deg);opacity:1}}
.plate .moment.key.go .m-icon,.plate .moment.star.go .m-icon,.plate .moment.place.go .m-icon{animation:m-pop .5s cubic-bezier(.2,1.4,.45,1) both}
@keyframes m-pop{0%{transform:scale(.35);opacity:0}100%{transform:scale(1);opacity:1}}
.plate .moment .m-icon.fly{animation:none;transition:transform .6s cubic-bezier(.4,0,.8,.6),opacity .2s ease-in .48s}
.plate .moment .m-label{font-family:Georgia,serif;font-size:clamp(17px,5vw,22px);letter-spacing:.08em;
  text-transform:uppercase;color:var(--bright);text-align:center;padding:0 18px;
  text-shadow:0 2px 10px rgba(0,0,0,.95);animation:m-pop .45s .15s ease-out both}
.plate .moment .m-sum{position:relative;text-align:center;padding:2px 22px 0;max-width:34em;
  font:clamp(14px,3.9vw,16px)/1.45 Georgia,serif;color:var(--ink);text-shadow:0 2px 8px rgba(0,0,0,.95);
  animation:m-pop .45s .3s ease-out both}
/* ONE TYPEFACE ON THE MOMENT. The two small labels were monospace — a coder's face, and on Cyrillic
   a fallback that sat beside Georgia like a different screen — and Georgia's old-style figures made
   "0/1" smaller than the words around it. Now: Georgia throughout, the labels as small capitals
   spaced out (the game's label), and lining figures wherever a number is read. */
.plate .moment .m-sum p{margin:3px 0;font-variant-numeric:lining-nums tabular-nums}
.plate .moment .m-sum p.helps{color:var(--bright);font-style:italic;margin-bottom:14px}
.plate .moment .m-sum p.sec-title,.plate .moment .m-kind{font:600 clamp(12px,3.3vw,13px)/1.4 Georgia,serif;
  letter-spacing:.18em;text-transform:uppercase;color:var(--accent)}
.plate .moment .m-sum p.sec-title{margin:0 0 4px;padding-top:10px;border-top:1px solid rgba(232,165,74,.35);
  display:inline-block}
.plate .moment .m-kind{display:block;margin-bottom:6px}
.plate .moment .m-sum b.n{font:700 1.12em/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  color:var(--bright);margin-left:2px}
.plate .moment .m-sum p.m-look{color:var(--bright);font-size:.92em;margin-top:-1px}
/* `.look` is the thing panel's class — a line named after it came out as a framed box. */
.plate .moment.tight{gap:4px}
.plate .moment.tight .m-icon{width:72px;height:72px}
.plate .moment.tight .m-sum p.sec-title{padding-top:4px}
.plate .moment.tighter .m-icon{width:44px;height:44px}
.plate .moment.tighter .m-label{font-size:16px}
.plate .moment.tighter .m-sum{font-size:13px;line-height:1.3}
.plate .moment.tighter .m-sum p.helps{margin-bottom:6px}
@media (prefers-reduced-motion:reduce){
  .plate .moment.go .m-sweep{animation:none;opacity:0}
  .plate .moment.go .m-icon,.plate .moment .m-label,.plate .moment .m-sum{animation:none}
  .plate .moment .m-icon.fly{transition:opacity .3s}
}
/* A TOKEN THAT HAS JUST ARRIVED is invisible until the mark that carries it lands, then pulses
   twice and wears a dot until the list is opened. */
.earned .tok.incoming{opacity:0}
.earned .tok.fresh{position:relative;animation:tok-pulse 1.1s ease-out 2}
.earned .tok.fresh::after{content:'';position:absolute;top:-1px;right:-1px;width:9px;height:9px;
  border-radius:50%;background:var(--accent);box-shadow:0 0 0 2px var(--bg)}
@keyframes tok-pulse{0%{box-shadow:0 0 0 0 rgba(232,165,74,.75)}100%{box-shadow:0 0 0 12px rgba(232,165,74,0)}}
@media (prefers-reduced-motion:reduce){ .earned .tok.fresh{animation:none} }

.plate .loading{position:absolute;inset:0;display:grid;place-items:center;align-content:center;
  gap:12px;color:var(--muted);font-size:13px;font-style:italic;z-index:2;pointer-events:none;
  padding:0 18px;text-align:center}
/* must come after .loading: a display:grid class otherwise beats [hidden] and the label stays */
.plate .loading[hidden]{display:none}

/* The picture is being drawn and that takes time — the plate has to MOVE, or it reads as a
   frozen game. A wave crossing the plate costs less than a spinner and feels like painting. */
.plate.busy{background:linear-gradient(105deg,var(--panel2) 30%,var(--panel) 50%,var(--panel2) 70%);
  background-size:280% 100%;animation:plateWave 2.4s ease-in-out infinite}
@keyframes plateWave{from{background-position:180% 0}to{background-position:-80% 0}}
.plate.busy .spark{width:34px;height:34px;border-radius:999px;border:2px solid var(--line);
  border-top-color:var(--accent);animation:plateSpin 1s linear infinite}
@keyframes plateSpin{to{transform:rotate(1turn)}}
/* There will be no picture: no spinner that never stops — just the calm name of the place. */
.plate.empty .spark{display:none}
.plate.empty .what{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:16px;font-style:normal;color:var(--muted);
  letter-spacing:1px}
@media(prefers-reduced-motion:reduce){
  .plate.busy{animation:none}
  .plate.busy .spark{animation-duration:2.4s}
}
/* AND IT HAS TO BE SEEN. The wave was panel on panel2 — two near-blacks — and the ring was 34px in
   the line colour, so on the dark plate a family saw neither and read the plate as broken. The
   wave now carries a breath of the accent, and the ring is larger and gold. */
.plate.busy{background:linear-gradient(105deg,var(--panel2) 28%,color-mix(in srgb,var(--accent) 16%,var(--panel)) 50%,var(--panel2) 72%);
  background-size:280% 100%}
.plate.busy .spark{width:46px;height:46px;border-width:3px;border-color:color-mix(in srgb,var(--accent) 22%,transparent);
  border-top-color:var(--accent)}
.plate.busy .loading .what{color:var(--bright);opacity:.8}
/* A moment on the plate has its own square that says it is waiting; the plate's label under it was
   cut in two by it («цртам … зида…»). */
.plate:has(.moment:not([hidden])) .loading{visibility:hidden}
/* THE MOMENT'S SQUARE WHILE ITS DRAWING IS ON THE WAY: the same wave and ring as the plate. */
.plate .moment .m-icon.pic.wait{display:grid;place-items:center;
  background:linear-gradient(105deg,var(--panel2) 28%,color-mix(in srgb,var(--accent) 18%,var(--panel)) 50%,var(--panel2) 72%);
  background-size:280% 100%;animation:plateWave 2.4s ease-in-out infinite}
.plate .moment .m-icon.pic.wait .spark{width:38px;height:38px;border-radius:999px;border:3px solid color-mix(in srgb,var(--accent) 22%,transparent);
  border-top-color:var(--accent);animation:plateSpin 1s linear infinite}
.plate .moment .m-icon.pic .m-mono{display:grid;place-items:center;width:100%;height:100%;
  font-family:Georgia,serif;font-size:clamp(44px,12vw,64px);color:var(--accent);background:var(--panel2)}
.plate .moment .m-icon.pic img{animation:m-fade .35s ease-out}
@keyframes m-fade{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){
  .plate .moment .m-icon.pic.wait{animation:none}
  .plate .moment .m-icon.pic.wait .spark{animation-duration:2.4s}
  .plate .moment .m-icon.pic img{animation:none}
}

/* ---- storytelling with arrows ---- */
/* NO GUTTERS. Going back lives in the band below now, so the words get the whole panel. */
.story{position:relative;flex:1 1 auto;min-height:0;display:flex;align-items:stretch;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  padding:14px 18px 26px}
/* The box must hold its full height even when the text is short: it is what we measure to see
   how many sentences fit on a page. If it shrank to its content, the measurement would depend
   on the previous scene and everything would come out as "one page". */
/* NO SCROLLBAR. It appeared over the text and took a strip of the line width with it, which on a
   phone costs a word a line. The way this game says "there is more below" is the way the hero's
   own description says it: the text fades out at the bottom and one line underneath says so. */
.story-text{flex:1;min-width:0;height:100%;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;font-size:17px;line-height:1.5;scrollbar-width:none}
.story-text::-webkit-scrollbar{display:none}
.story-text .body{display:block;white-space:pre-line}
.story-text.more{mask-image:linear-gradient(180deg,#000 86%,transparent);
  -webkit-mask-image:linear-gradient(180deg,#000 86%,transparent)}
.story-text .who{display:block;font-size:inherit;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--accent);margin-bottom:6px}
.story-text.small{font-size:15px}

/* A CARD: the heading is the `who`, then the name of the person it is about, then the body, and
   under it whatever the narrator saw while they spoke. The name is the one place in the story box
   where a line is bold and white — it is a label, not prose, and it has to be findable at a
   glance in a panel a family reads once. */
.story-text .lead{display:block;font-weight:700;color:#fff;font-size:clamp(16px,4.3vw,18px);
  line-height:1.3;margin:0 0 8px}
/* THE SPEAKER'S FACE IS ON THE PICTURE, NOT BESIDE THE WORDS. A small circle used to float at
   the left of the quote; now that the plate above shows whoever is speaking, the same face twice
   in one view is one picture too many — and the small one is the one that says least. What is
   left here is the column rule the float needed, because the body still wants its own formatting
   context and the narrator's block below it still wants the full width. */
.story-text .body{overflow:hidden}
.story-text .note{clear:both}

/* DID THE THING YOU PRESSED HAPPEN. The answer to the question the press asked, set apart from
   the prose and from the quiet lines under it — a family read a whole paragraph and could not
   tell. A bar down the side in the colour of the answer, because "it worked" and "that did not
   happen" are two different pieces of news and the screen should not make you read to find out
   which one it is.
   IT GOES FIRST, above the prose. It was under it, and a family read a paragraph about a beam
   and a captain and had to reach the bottom to learn whether the beam had moved. The answer
   comes first and the narrator explains it afterwards, which is the order anybody tells this
   kind of news in. */
.story-text .verdict{display:block;clear:both;margin:0 0 14px;padding:9px 12px;
  border-left:3px solid var(--accent);border-radius:0 10px 10px 0;
  background:rgba(232,165,74,.09);font-size:inherit;line-height:inherit}
/* A SUCCESS IS GREEN, so it is told apart from a choice (gold) and a miss (red) before a word
   of it is read. */
.story-text .verdict.yes{border-left-color:var(--good);background:rgba(127,216,143,.10)}
.story-text .verdict.yes .verdict-how{color:var(--good)}
.story-text .verdict.no{border-left-color:var(--danger);background:rgba(224,85,69,.10)}
.story-text .verdict.half{border-left-color:var(--muted);background:rgba(143,127,184,.10)}
/* ONE SIZE IN THE STORY BOX. The verdict and the line under the rule were 14–15px under prose of
   17 (and 18–21 on a desk), so the answer to "did it work" read as the small print. They are set
   apart by colour, bar and rule — never by being smaller. */
.story-text .verdict-how{display:block;font-size:inherit;letter-spacing:.09em;
  text-transform:uppercase;color:var(--accent);margin-bottom:3px}
.story-text .verdict.no .verdict-how{color:var(--danger)}
.story-text .verdict.half .verdict-how{color:var(--muted)}
.story-text .verdict-what{display:block;color:var(--bright)}
.story-text .verdict-then{display:block;margin-top:5px;color:var(--ink)}
.story-text .verdict-gains{display:block;margin-top:8px;color:var(--accent);white-space:pre-line}

/* WHAT THE EVENING JUST SPENT OR WON. Not narration — the screen's own line about the meter and
   about what was learned — so it is set apart and in the accent, the way every other thing this
   game says in its own voice is. Glued to the end of the prose it was read as the narrator's last
   sentence, which is exactly what it must not be. */
.story-text .earned{display:block;clear:both;margin-top:14px;padding-top:12px;
  border-top:1px solid var(--line);color:var(--accent);
  font-size:inherit;line-height:inherit;white-space:pre-line}
.story-text .note{display:block;margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.story-text .note-who{display:block;font-size:inherit;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--accent);margin-bottom:5px}
.story-text .note-text{display:block;color:var(--ink)}

/* THERE IS MORE BELOW, said in words rather than with a bar. Same as the hero's description:
   the text fades and one line underneath says so, and both go the moment they reach the end. */
.story .scroll-cue{position:absolute;left:0;right:0;bottom:5px;pointer-events:none;
  text-align:center;color:var(--accent);font-size:clamp(13px,3.3vw,14px);line-height:1;opacity:0;
  transition:opacity .18s}
.story .scroll-cue.on{opacity:.9}
/* GOING BACK SITS BESIDE THE PAGE COUNT, in the band below the story — not in the story's own
   margins, where two 44px gutters cost a word off the end of every line on a phone and were
   reserved even on pages that had nothing either side of them. One place for the count and both
   directions; the words get the whole panel. */
/* The band under the story: back, then the count and the way on. */
.pagerow{display:flex;gap:8px;align-items:stretch}
.pagerow .act{flex:1 1 auto;min-width:0}

.pageback{flex:0 0 auto;width:44px;align-self:stretch;display:grid;place-items:center;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  color:var(--muted);font-size:22px;padding:0}
.pageback[hidden]{display:none}
.story[hidden]{display:none}
.story-dots{position:absolute;bottom:8px;left:0;right:0;text-align:center;font-size:13px;
  letter-spacing:3px;color:var(--muted);pointer-events:none}
/* while waiting for a tap: the story holds still and the reader sees they should touch it */
.story .tap-hint{position:absolute;bottom:6px;right:44px;font-size:13px;letter-spacing:1px;
  color:var(--accent);opacity:0;transition:opacity .3s;pointer-events:none}
/* On the last page there is no hint — a real button in the band below the story takes over. */
.story.await-tap{cursor:pointer}
.story.await-tap .tap-hint{opacity:1;animation:tapPulse 1.6s ease-in-out infinite}
@keyframes tapPulse{50%{opacity:.35}}
@media(prefers-reduced-motion:reduce){.story.await-tap .tap-hint{animation:none}}

/* ---- sheets (quests, hero) ---- */
/* A sheet covers the page completely — a translucent backdrop let the title and the story
   bleed through the hero and the text became unreadable. */
.sheetover{position:fixed;inset:0;z-index:30;background:var(--bg);display:flex;
  flex-direction:column;padding:var(--pad)}
.sheetover[hidden]{display:none}
/* Full screen, but the content stays in a phone-width column: without this the squares swell
   to 300px on a desktop and the sheet falls off the bottom edge. */
.sheetover > *{width:100%;max-width:560px;margin-left:auto;margin-right:auto}
/* `margin:auto` sideways, or the title sits on the window's left edge while the column under it is
   centred: this rule outranks the column rule above and used to zero both sides. */
.sheetover h2{margin:6px auto 12px;font-size:18px;letter-spacing:3px;color:var(--bright);font-weight:normal}
.sheetover .body{flex:1;min-height:0;overflow-y:auto}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:14px}

/* ---- the phone frame (Ctrl+M) ---- */
body.device{background:#0a0714;display:grid;place-items:center;height:100dvh}
body.device #app{width:393px;height:852px;max-height:calc(100dvh - 40px);border:10px solid #241b3e;
  border-radius:44px;box-shadow:0 24px 60px rgba(0,0,0,.6);overflow:hidden}
/* A LABEL MAY NOT SWALLOW A BUTTON. This pill is fixed in the bottom-left corner — which is
   exactly where the band under the story puts the page counter and the way back — so on a
   desktop it sat ON the control and every press landed on the pill instead. Measured: a real
   click at the centre of "1 / 4 ▸" was delivered to `DIV#deviceHint`, the button received no
   event at all, and the arrow keys (which reach `go()` without going through the screen) moved
   the page perfectly. It only showed in Serbian and Italian because those labels are wider than
   the English one and reach the middle of the row; in English it was quietly killing the back
   arrow instead. Nothing on it is meant to be pressed, so nothing on it accepts a press. */
/* AND A FULL-SCREEN PANEL STAYS INSIDE THE PHONE. It is a sibling of #app, fixed to the window, so
   in the frame it spread over the whole desktop. Placed over the frame's inner screen instead. */
body.device .sheetover{inset:auto;top:50%;left:50%;transform:translate(-50%,-50%);
  width:373px;height:calc(min(852px, 100dvh - 40px) - 20px);border-radius:34px;overflow:hidden}
#deviceHint{position:fixed;left:12px;bottom:12px;z-index:40;pointer-events:none;
  font-family:ui-monospace,monospace;
  font-size:13px;color:var(--muted);background:rgba(20,15,38,.85);border:1px solid var(--line);
  border-radius:999px;padding:5px 12px}

/* ---- the setup screens ----
   Everything fits without scrolling: one row per decision, label left, chips right.
   From the supplied guide we took the banner in the header, the cream explanation card and the
   green button that moves you on — the game's own colours stay as they are. */
.setup .top{gap:8px}
.setup .banner{flex:1;min-width:0;text-align:center;padding:7px 14px;border-radius:14px;
  background:linear-gradient(180deg,#ffc766,#e79a28);border:1px solid #ffe0a8;
  box-shadow:0 3px 0 #a96c12,0 6px 14px rgba(0,0,0,.35)}
.setup .banner b{display:block;font-size:15px;letter-spacing:2px;color:#3b2a12;font-weight:normal;
  line-height:1.15}
.setup .banner span{display:block;font-size:13px;letter-spacing:2px;color:#7a5518;text-transform:uppercase}
.setup .top .icon{font-size:22px}

/* THE BRIEFING WEARS THE SAME GOLD FRAME as the screens the family has just come through — it is
   the last of them, not the first room. The name must fit whole, so the type shrinks (see
   `fitTitle`) rather than the name being cut, and it is centred in the frame either way. */
/* AS TALL AS THE FRAMES BEFORE IT. Those carry a title and a line under it; this one carries only
   the adventure's name, and a frame that shrinks between two screens reads as a different frame.
   The name may take a second line — there is room for it, which is why it is allowed here and not
   over a picture. */
.top h1.banner{flex:1;min-width:0;margin:0;text-align:center;padding:7px 14px;border-radius:14px;
  min-height:calc(2 * 1.15em + 14px);display:flex;align-items:center;justify-content:center;
  overflow:hidden;
  background:linear-gradient(180deg,#ffc766,#e79a28);border:1px solid #ffe0a8;
  box-shadow:0 3px 0 #a96c12,0 6px 14px rgba(0,0,0,.35);
  font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-weight:normal;letter-spacing:2px;color:#3b2a12;
  text-transform:uppercase;line-height:1.15;font-size:var(--banner-size,15px)}
/* A TEXT NODE IN A FLEX BOX IS SIZED TO ITS MAX-CONTENT and simply runs off both edges — which is
   what "TESLA AND THE IMPOSSIBLE SIGNAL" did. In a span it wraps like text — but only once the
   base `.top h1` rule's `white-space:nowrap` is undone: that rule exists to put an ellipsis on a
   long ROOM name in a narrow header, which is the opposite of what this frame wants. */
.top h1.banner{white-space:normal;text-overflow:clip}
.top h1.banner > span{display:block;max-width:100%}

.step{gap:0;padding-bottom:4px;overflow-y:auto}
/* must be explicit: .stretch carries display:flex, which would otherwise beat the hidden
   attribute and a hidden step would still occupy half the screen */
.step[hidden]{display:none}
.step > *{flex:0 0 auto}

/* cream cards: the opening explanation and the description of whatever was just chosen */
.card.note,.card.desc{background:var(--cream);border:1px solid #d8c49a;border-radius:14px;
  color:var(--cream-ink);padding:9px 12px;font-size:13px;line-height:1.35;text-align:center;
  box-shadow:0 3px 0 rgba(0,0,0,.25)}
.card.note{margin-bottom:8px}
/* the description hugs the bottom of the step, right above the button — otherwise a gap yawns */
.card.desc{margin-top:auto;min-height:52px;display:flex;align-items:center;justify-content:center}

#heroPlate{flex:1 1 auto;min-height:120px}
#heroPlate .redraw{position:absolute;right:8px;bottom:8px;z-index:3;width:44px;height:44px;
  border-radius:14px;border:1px solid var(--accent);background:rgba(20,15,38,.85);
  color:var(--bright);font-size:20px;line-height:1;padding:0}
#heroPlate .redraw[hidden]{display:none}
#heroPlate .redraw:active{background:#3a2a18}

/* Five or six choices are rows separated by a hairline — without it everything runs together
   into a heap of chips. The label sits against the FIRST row of chips, not centred: when the
   chips wrap onto two rows a centred label ends up between them and looks like it belongs to
   the wrong group. */
.pick{display:flex;align-items:flex-start;gap:10px;padding:4px 0;border-top:1px solid var(--line)}
.pick:first-child{border-top:none}
.pick .label{flex:0 0 58px;margin:0;padding-top:12px;text-align:left;font-size:13px;letter-spacing:2px}
.pick .chips{flex:1;justify-content:flex-start}
/* 40px is the floor for a child's finger — squeeze the picture rather than the targets */
.pick .chip{min-height:40px;font-size:13.5px;padding:0 12px}
/* the typing field shares the row with the chips, so it reads as one more choice */
.pick .fill{flex:1;min-width:0;display:flex}
.pick .fill input{flex:1;min-width:0;min-height:40px;background:var(--panel2);
  border:1px solid var(--line);border-radius:999px;color:var(--ink);font-size:15px;padding:0 14px}
.pick .fill input:focus{outline:none;border-color:var(--accent)}
.pick .fill input::placeholder{color:var(--muted);font-style:italic}

.chips{display:flex;flex-wrap:wrap;gap:7px;justify-content:center}
.chip{min-height:40px;padding:0 14px;border-radius:999px;border:1px solid var(--line);
  background:var(--panel2);color:var(--ink);font-size:14px;box-shadow:0 2px 0 rgba(0,0,0,.3)}
.chip.on{border-color:var(--accent);background:linear-gradient(180deg,#5a3c14,#3a2a18);
  color:var(--bright);box-shadow:0 2px 0 #7a5518}
.chip:active{transform:translateY(1px);box-shadow:none}
.label{font-size:13px;letter-spacing:3px;text-transform:uppercase;color:var(--muted);text-align:center;margin-top:2px}
.hint{font-size:13px;color:var(--muted);line-height:1.4;text-align:center;min-height:17px}
.hint.center{text-align:center}
/* Nothing to say, nothing on screen. `min-height` reserves the row so a message appearing does
   not shift the layout — but an empty one still drew its own box. */
#status:empty{display:none}

/* the button that moves you on — the only green thing on screen, so it cannot be missed */
.act.go{justify-content:center;font-size:18px;letter-spacing:1px;color:#1f2e08;
  background:linear-gradient(180deg,var(--go),var(--go2));border-color:#b6e06a;
  box-shadow:0 3px 0 #4e7a1c}
.act.go:active{transform:translateY(2px);box-shadow:0 1px 0 #4e7a1c}
.act.go:disabled{opacity:.6}

/* The "welcome" step is deliberately sparse: two choices and a lot of air around them. */
.step.hello{justify-content:center;gap:10px}
.step.hello .pick{border-top:none;padding:0}
.step.hello .pick .label{padding-top:14px}
.step.hello .card.note{margin-bottom:14px}
.step.hello .chip{min-height:44px;font-size:15px}
.step.hello .fill input{min-height:48px;font-size:17px;text-align:center}

/* Steps full of choices breathe: label ABOVE the chips, everything centred, and the spare
   height shared between the groups. With the label to one side everything crowded against the
   top and left half the screen empty below. */
/* `space-between` was written for six rows of choices. With three it pushes the first to the
   ceiling, the last onto the green button and leaves two lakes of nothing in between — which
   reads as a page that failed to load. Centred with a generous, CAPPED gap keeps the group
   together and lets the empty space fall above and below it, where empty space is calm. */
.step.airy{justify-content:center;gap:clamp(14px,5vh,42px)}
/* An empty note is not a note. */
.step.airy .card.note:empty{display:none}
.step.airy .pick{flex-direction:column;align-items:stretch;gap:7px;border-top:none;padding:0}
.step.airy .pick .label{flex:none;padding:0;text-align:center;font-size:13px;letter-spacing:3px}
.step.airy .chips{justify-content:center}
.step.airy .chip{min-height:44px;font-size:14.5px;padding:0 15px}
.step.airy .card.desc{margin-top:0}

/* The "your hero" step: the picture across the whole page and nothing else. The redraw button
   is TOP RIGHT on the picture itself — that is where it is looked for — and below there is
   only "continue". */
.herostep{padding-bottom:4px}
.herostep #heroPlate{flex:1 1 auto;min-height:0;margin:0}
.herostep #heroImg{object-fit:cover;object-position:50% 22%}
.herostep .redraw{top:10px;right:10px;bottom:auto}

/* ---- the adventure carousel ----
   Swiping is native horizontal scrolling with scroll-snap: it works with a finger, a trackpad
   and the arrow keys, with no library and no touch handling. One card = one rail width. */
.advstep{gap:8px;overflow:hidden}
.advrail{flex:1 1 auto;min-height:0;display:flex;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scroll-behavior:smooth;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;scrollbar-width:none}
.advrail::-webkit-scrollbar{display:none}
.advcard{flex:0 0 100%;scroll-snap-align:center;scroll-snap-stop:always;
  display:flex;flex-direction:column;gap:8px;min-width:0;padding:0 2px}
/* with no illustration a card has nothing to stretch — the content goes to the middle instead
   of huddling at the top and leaving half the screen empty */
.advcard:not(:has(.advart)){justify-content:center}
.advart{flex:1 1 auto;min-height:120px}
.advart img{object-position:50% 40%}
.advtitle{flex:0 0 auto;text-align:center;font-size:19px;letter-spacing:1px;color:var(--bright);
  line-height:1.2}
.advhook{flex:0 0 auto;background:var(--cream);border:1px solid #d8c49a;border-radius:14px;
  color:var(--cream-ink);padding:9px 12px;font-size:13px;line-height:1.35;text-align:center;
  box-shadow:0 3px 0 rgba(0,0,0,.25)}
.advadd{flex:0 0 auto;min-height:44px;background:var(--panel2);border:1px solid var(--line);
  border-radius:999px;color:var(--ink);font-size:15px;padding:0 14px;text-align:center}
.advadd:focus,.advown:focus{outline:none;border-color:var(--accent)}
.advadd::placeholder,.advown::placeholder{color:var(--muted);font-style:italic}
/* the fifth card: no picture, so all the room goes to the field where the player writes */
.advcard.own{justify-content:flex-start}
.advown{flex:1 1 auto;resize:none;background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius);color:var(--ink);font-size:16px;line-height:1.4;padding:12px 14px}
.advcard.note{justify-content:center}

.advdots{flex:0 0 auto;display:flex;justify-content:center;gap:7px;padding:2px 0 0}
.advdots i{width:8px;height:8px;border-radius:999px;background:var(--line);transition:.25s}
.advdots i.on{background:var(--accent);width:20px}

/* On short screens we squeeze the rows; if that is still not enough, the step scrolls rather
   than letting the content overlap itself. */
/* Below ~700px of height the airy layout no longer fits: the label moves back to the side and
   the rows tighten up. Dense beats scrolling. */
@media (max-height:700px){
  .step.airy{justify-content:flex-start;gap:5px}
  .step.airy .pick{flex-direction:row;align-items:flex-start;gap:10px}
  .step.airy .pick .label{flex:0 0 58px;padding-top:12px;text-align:left;
    font-size:13px;letter-spacing:2px}
  .step.airy .chips{justify-content:flex-start}
  .step.airy .chip{min-height:40px;font-size:13.5px;padding:0 12px}
  .step.airy .card.desc{margin-top:auto}
}

@media (max-height:760px){
  .step .pick{padding:2px 0}
  .step .pick .label{padding-top:10px}
  .card.note{padding:6px 10px;font-size:13px}
  .card.desc{min-height:44px;padding:6px 10px;font-size:13px}
}

/* ---- the world map ---- */
.card.place{flex:0 0 auto;background:var(--cream);border:1px solid #d8c49a;border-radius:14px;
  color:var(--cream-ink);padding:10px 14px;box-shadow:0 3px 0 rgba(0,0,0,.25);
  min-height:76px;display:flex;flex-direction:column;justify-content:center;gap:3px}
.card.place .place-name{font-size:17px;letter-spacing:.5px;font-weight:normal;line-height:1.2}
.card.place .place-desc{font-size:13.5px;line-height:1.4}
/* while you are nowhere: a calm, empty card with an instruction and no fake title */
.card.place.empty{align-items:center;text-align:center;font-size:13.5px;font-style:italic;
  background:transparent;color:var(--muted);border-style:dashed;border-color:var(--line);
  box-shadow:none}
/* The map is shortened so the description below it gets a proper card — not a line of small
   text beside a button. Everything you know about a place before entering fits in that card. */
#mapWrap{flex:1 1 auto;min-height:150px}
#mapWrap canvas{position:absolute;inset:0;width:100%;height:100%;cursor:pointer;z-index:2}
#mapWrap img{object-fit:cover}
.where{text-align:center;font-size:13px;color:var(--muted);min-height:18px}
.where b{color:var(--bright);font-weight:normal}

/* ---- listovi ---- */
.herocard{display:flex;gap:12px;align-items:center;margin-bottom:10px}
.herocard .face{flex:0 0 72px;width:72px;height:72px;border-radius:14px;overflow:hidden;
  border:1px solid var(--line);background:var(--panel2)}
.herocard .face svg{width:100%;height:100%;display:block}
.hname{font-size:17px;color:var(--bright)}
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;text-align:center;margin-bottom:10px}
.stats b{display:block;font-family:ui-monospace,monospace;font-size:16px;color:var(--bright);font-weight:normal}
.stats span{font-size:13px;letter-spacing:1px;color:var(--muted)}
.bag{margin:8px 0 0;padding:0;list-style:none;font-size:14px}
.bag li{padding:6px 0;border-top:1px solid var(--line)}
.bag li:first-child{border-top:none}

/* ---- strana lokacije ---- */
#scenePlate{flex:0 0 auto;height:clamp(170px,34dvh,340px)}
#story{flex:1 1 auto;min-height:0}
/* The "i" button: bring back the description just read. It sits in the bottom right corner of
   the PICTURE — not in the row with the actions, where the send key already took its place, and
   not in the gap between picture and actions, where it looked like it belonged to nothing. */
/* AND THE `i` TAKES THE CORNER THE BAG LEFT. It sat bottom-right, which is now where the ways
   on are — and of the two, the one you reach for while deciding is the door. */
.recap{position:absolute;right:10px;top:40px;z-index:4;width:40px;height:40px;border-radius:999px;
  border:1px solid var(--line);background:rgba(20,15,38,.86);color:var(--accent);
  font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:18px;font-style:italic;line-height:1;padding:0}
.recap[hidden]{display:none}
.recap:active{background:#3a2a18;border-color:var(--accent)}

/* ==== THE HINT LAMP ====
   Top right of the picture, opposite the "i" — one is a way back into what was said, the other
   a way forward when nobody knows what to do. Both are small on purpose: they are exits, not
   the game. The badge counts the rungs still on the ladder, so a table can see that help is
   finite before it spends it. */
/* THE LAMP MOVED INTO THE HEADER (far right) when the bag took the picture's corner. It keeps its
   own class because it keeps its own states — spent, disabled, and a badge that counts rungs —
   but the header positions it now, not the plate. */
.lamp{position:relative;z-index:6;width:44px;height:44px;border-radius:999px;
  border:1px solid var(--line);background:rgba(20,15,38,.86);color:var(--bright);
  font-size:20px;line-height:1;padding:0;cursor:pointer}
.lamp[hidden]{display:none}
.lamp:active{background:#3a2a18;border-color:var(--accent)}
.lamp:disabled{opacity:.6}
/* The last rung is out. The lamp stays — the three hints must still be re-readable — but it
   stops looking like an offer. */
.lamp.spent{filter:grayscale(1);opacity:.55}
.lamp .badge{position:absolute;top:-4px;right:-2px;background:var(--accent);color:#2a1a08;
  font-family:ui-monospace,monospace;font-size:13px;border-radius:999px;padding:1px 5px}
.lamp .badge[hidden]{display:none}

/* What is already out, laid over the picture — the choices underneath are never disturbed, so
   the table can read a hint and act on it without anything moving. */
/* ==== THE ADVENTURE LOG ====
   A reminder, read in three or four turns of the page. Every group carries the face of the person
   who said it or the picture of the room it was in, because a heading with a picture beside it is
   what keeps a page of facts from reading as a wall. */
.adventurelog h3{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:15px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--accent);margin:0 0 8px}
.adventurelog .logpursuing{font-size:13px;color:var(--muted);border-left:2px solid var(--accent);
  padding-left:8px;margin-bottom:10px}
.adventurelog .logpursuing[hidden]{display:none}
.adventurelog .logpages{display:flex;flex-direction:column;gap:12px}
.adventurelog .logentry b{color:var(--bright);font-weight:600}
/* A CONCLUSION IS THE THING PEOPLE STOP AND READ, so it is the one entry with a mark of its own. */
.adventurelog .logentry.learned{border-left:2px solid var(--accent);padding-left:10px;
  font-size:clamp(14px,3.6vw,15px);line-height:1.4}
.adventurelog .loghead{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.adventurelog .logpic{flex:0 0 34px;width:34px;height:34px;border-radius:8px;overflow:hidden;
  background:var(--panel2);border:1px solid var(--line)}
.adventurelog .logpic img{position:static;inset:auto;opacity:1;
  width:100%;height:100%;object-fit:cover;display:block}
.adventurelog .logentry ul{margin:0;padding-left:44px;list-style:none}
/* 13px is the floor for anything a family reads; these are short lines and they still are. */
.adventurelog .logentry li{font-size:clamp(13px,3.4vw,14px);line-height:1.35;color:var(--ink);
  position:relative;padding-left:12px;margin:3px 0}
.adventurelog .logentry li::before{content:'·';position:absolute;left:0;color:var(--muted)}
.adventurelog .logentry p{margin:2px 0 0 44px;font-size:13px;color:var(--muted);line-height:1.3}
.adventurelog .logpager{display:flex;align-items:center;justify-content:center;gap:14px;
  margin-top:12px}
.adventurelog .logpager[hidden]{display:none}
.adventurelog .logpager button{min-width:44px;min-height:44px;border-radius:12px;
  border:1px solid var(--line);background:transparent;color:var(--ink);font-size:20px;cursor:pointer}
.adventurelog .logpager button:disabled{opacity:.3}
.adventurelog .logcount{font:13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted)}

/* ==== WHAT IS WITHIN REACH ====
   The same sheet of glass over the picture as the hint panel, and the same furniture — this is
   the room's own contents, not a different kind of thing. What is different is the shape of the
   list: a row of things, each a picture with its name under it, because it is read at a glance
   and by somebody who may not read. Targets are 44px+ like everything else. */
.reachpanel{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;
  background:rgba(11,7,22,.94);border-radius:14px;padding:10px 12px 12px;overflow:hidden}
.reachpanel[hidden]{display:none}
/* A GRID ITEM STRETCHES TO ITS ROW unless told not to, and a square picture inside a stretched
   box is a square measured from the wrong side: the icons came out 285px tall in an 84px column
   and the panel was three tall empty rectangles. `align-items:start` lets each thing be its own
   height, and then the 1:1 picture is 1:1 of the COLUMN. */
.reachlist{flex:0 1 auto;min-height:0;overflow-y:auto;margin-top:8px;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:10px;
  align-content:start;align-items:start}
.reachlist .prop{display:flex;flex-direction:column;align-items:center;gap:6px;min-height:44px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel2);padding:8px 6px;
  color:var(--ink);font:inherit;cursor:pointer;text-align:center}
.reachlist .prop:active{border-color:var(--accent)}
.reachlist .prop .pic{width:100%;aspect-ratio:1/1;border-radius:8px;overflow:hidden;
  background:linear-gradient(160deg,#1a1330,#0e0a1e);display:flex;align-items:center;
  justify-content:center;font-size:22px;color:var(--muted)}
/* AND IT HAS TO CLIMB OUT FROM UNDER `.plate img`. This panel lives inside the picture plate, and
   the plate's own rule puts every image in it at `position:absolute; inset:0; opacity:0` — that
   is the fade the room's picture arrives with, and it is applied by adding a class the plate's
   own loader adds. An icon written by hand never gets that class, so three loaded pictures sat
   invisible at the plate's full size inside 91px boxes. */
.reachlist .prop .pic img{position:static;inset:auto;opacity:1;
  width:100%;height:100%;object-fit:cover;display:block}
/* 13px is the floor for anything a family reads, and a label under a thumbnail is no exception. */
.reachlist .prop .name{font-size:clamp(13px,3.4vw,14px);line-height:1.2;color:var(--bright)}
.reachpanel .foot{flex:0 0 auto;margin-top:8px;font-size:13px;color:var(--muted);text-align:center}

/* OVER THE STRIPS, NOT UNDER THEM. It was z 5 and the people and the keys along the picture are
   z 6, so the faces were drawn across the hint and the ✦ 🎒 👁 keys sat on top of it — a panel
   that covers the whole picture has to cover what is on the picture. Below the die (12). */
.hintpanel{position:absolute;inset:0;z-index:8;display:flex;flex-direction:column;
  background:rgba(11,7,22,.94);border-radius:14px;padding:10px 12px 12px;overflow:hidden}
.hintpanel[hidden]{display:none}
.hinthead{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding-right:46px;flex:0 0 auto}
.hinthead b{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:14px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--accent)}
.hintclose{width:34px;height:34px;border-radius:999px;border:1px solid var(--line);
  background:transparent;color:var(--muted);font-size:20px;line-height:1;padding:0;cursor:pointer}
/* The list takes only the room it needs and scrolls when it outgrows the plate, so three short
   hints do not float in the middle of a tall empty panel. */
.hintlist{flex:0 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:10px;
  margin-top:8px}
/* Read as a ladder: everything ranged left, whatever the plate's own alignment happens to be.
   SCOPED TO THE LADDER. Written bare, this rule also found `#status.hint` on the setup page and
   gave an EMPTY div a 2px left border and a 17px minimum height — which is the thin vertical
   line that appeared in the bottom-left corner of every setup screen. */
.hintlist .hint{border-left:2px solid var(--line);padding:2px 0 2px 10px;text-align:left}
/* The rung this press just bought. Everything above it was already known. */
.hintlist .hint.fresh{border-left-color:var(--accent)}
.hint b{display:block;font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.hint p{margin:2px 0 0;font-size:14px;line-height:1.4;color:var(--ink)}
.hint.fresh p{color:var(--bright)}
.hint em{font-size:13px;font-style:normal;color:var(--muted)}
.hintfoot{flex:0 0 auto;padding:10px 0 0 12px;font-size:13px;color:var(--muted);text-align:left}

/* WHAT NOW: the picture darkens and one clear thing is said in the middle of it — the part still to
   do, the hint in large letters, the step under it. Not a list: a list of rungs read as a list of
   what had already been done. A tap anywhere closes it. */
#hintPanel.hintpanel{background:rgba(6,4,14,.78);justify-content:center;align-items:center;
  padding:18px 22px;cursor:pointer;animation:hint-in .25s ease-out}
#hintPanel .hinthead{position:absolute;top:10px;left:14px;right:10px;padding-right:0}
#hintPanel .hintlist{flex:0 1 auto;max-width:34em;margin:0;gap:12px;align-items:center;text-align:center}
#hintPanel .hintlist p{margin:0;text-shadow:0 1px 3px rgba(0,0,0,.8)}
#hintPanel .hintlist .aim{font-size:clamp(14px,3.8vw,16px);color:var(--muted)}
#hintPanel .hintlist .say{font-family:Georgia,serif;font-size:clamp(18px,5vw,23px);line-height:1.35;color:var(--bright)}
#hintPanel .hintlist .step{font-size:clamp(15px,4.2vw,18px);line-height:1.4;color:var(--ink)}
#hintPanel .hintlist .easier{font-size:clamp(14px,3.8vw,16px);color:var(--accent)}
#hintPanel .hintfoot{position:absolute;left:0;right:0;bottom:12px;text-align:center;padding:0 12px}
@keyframes hint-in{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){#hintPanel.hintpanel{animation:none}}

/* Once the description has been read and an action is being chosen, the text disappears and
   the picture takes its place. Tied to the story's own visibility rather than a separate class,
   so the layout cannot drift out of step with the state and there is no flicker between turns. */
/* THE PICTURE GIVES WAY FIRST, AND THEN THE ROW SCROLLS. `flex-basis:0` means the plate is
   whatever is left over after the row — it grows into a comfortable screen and gives everything
   back on a cramped one — and the floor is a floor again: a row of six buttons is 419px that
   cannot shrink, so the old bargain (no minimum at all below 700px) crushed the picture to a
   77px strip at 640, with the "within reach" panel, which lives INSIDE the plate, crushed to 75
   with it. That is what reads as a panel sliding under the picture. Measured at 640: plate 77.
   A row that needs a small scroll is a far better trade than a photograph turned into a band. */
/* AND THE FLOOR HAS TO BE IN THE BASIS, not only in `min-height`. The plate's content is an
   absolutely-positioned image, so its natural basis is ZERO: it grows into whatever is left and
   `min-height` then clamps it AFTER the sum is settled, so it PAINTS 170 while flex gave it 95 —
   and the picture draws over the first button. Measured at 640: plate box 64..234, first button
   159..243, covered. With 170 as the basis the row is what gives way instead, and it can. */
.stretch:has(#story[hidden]) #scenePlate{flex:1 1 170px;height:auto;min-height:170px}
/* AND THE BAND BELOW IT MAY SHRINK, which is what lets the scroll happen at all: a band that is
   `flex:0 0 auto` pushes its overflow into the picture instead of scrolling its own content.
   IT IS A SIBLING OF `.stretch`, NOT A CHILD, and the root is an ID — written as
   `.stretch:has(…) .actions`, and then as `.app:has(…)`, the rule matched NOTHING both times, and
   the measurement said so: the row stayed 419px tall at every height while the picture was the
   only thing that moved. A selector is worth checking before the layout it is meant to change. */
#app:has(#story[hidden]) .actions{flex:0 1 auto;min-height:0}
/* The picture's floor has to hold at the column above it too, or the deficit lands there. */
#app:has(#story[hidden]) .stretch{min-height:170px}
#choices{min-height:0;overflow-y:auto;overscroll-behavior:contain}
/* It says when it scrolls, the same way the story box and the way-in page do. */
#choices.more{mask-image:linear-gradient(180deg,#000 88%,transparent);
  -webkit-mask-image:linear-gradient(180deg,#000 88%,transparent)}
/* What the row of buttons is asking. Small, quiet and directly above them — the story is put
   away by the time a choice is made, so without this a question asked in the story left two
   bare buttons behind it and nothing that said what they answered. */
.prompt{display:flex;flex-direction:column;gap:2px;padding:0 2px 2px}
.prompt[hidden]{display:none}
/* What a miss and a 1 would cost: under the stake, quieter than it, one line each. */
.prompt i.more{font-style:normal;white-space:pre-line;font-size:clamp(13px,3.6vw,14px);line-height:1.35;color:var(--muted);margin-top:4px}
.prompt i.more[hidden]{display:none}
.prompt i.more .now{color:var(--accent);font-weight:600}
/* Under a 1 on the die: what it costs, concretely. */
#diceOver .d-one{margin-top:6px;font-size:clamp(13px,3.6vw,15px);line-height:1.35;color:var(--danger);max-width:30ch;text-align:center}
.prompt b{font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.1em;
  text-transform:uppercase;color:var(--accent)}
.prompt span{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:15px;line-height:1.3;color:var(--bright)}

/* THE ROLL SCREEN'S OWN STRIP. Set apart from the sentence above it, because it answers a
   different question: not "may I" but "with what, and against what". Every size has the 13px
   floor; the two numbers are allowed to be bigger because they are the whole point of it. */
.rollcard{border:1px solid var(--line);border-radius:12px;background:var(--panel2);
  padding:8px 10px;margin:0 2px 8px;display:flex;flex-direction:column;gap:6px}
.rollcard[hidden]{display:none}
.rc-parts{display:flex;flex-wrap:wrap;gap:4px 6px}
.rc-parts[hidden]{display:none}
.rc-parts i{font:13px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace;font-style:normal;
  color:var(--muted);background:var(--panel);border:1px solid var(--line);
  border-radius:999px;padding:2px 8px;white-space:nowrap}
.rc-sum{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.rc-parts:not([hidden]) + .rc-sum{border-top:1px solid var(--line);padding-top:6px}
.rc-mine,.rc-need{font:clamp(13px,3.6vw,16px)/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:.04em}
.rc-mine{color:var(--bright)}
.rc-mine b{font-size:clamp(17px,5vw,21px);color:#fff}
.rc-need{color:var(--muted)}
.rc-need b{font-size:clamp(17px,5vw,21px);color:var(--accent)}
.rc-eased{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:13px;line-height:1.3;color:var(--accent)}
.rc-eased[hidden]{display:none}

/* A thing you took yourself, in the same bag as the adventure's kit — named, and visibly not
   part of what you were given. */
.slot.taken{border-style:dashed;border-color:var(--accent)}
.slot.taken .slot-name{color:var(--bright)}

#choices{display:flex;flex-direction:column;gap:8px}
#choices[hidden]{display:none}   /* display:flex would otherwise beat the hidden attribute */
.act[hidden]{display:none}       /* the same goes for buttons: .act is display:flex */
/* While reading, the "choose an action" button holds its place but stays invisible — see the
   comment in location.js */
#doneBtn{visibility:hidden;pointer-events:none;transition:opacity .25s;opacity:0}
#doneBtn.on{visibility:visible;pointer-events:auto;opacity:1}
.foe{position:absolute;left:10px;right:10px;bottom:10px;background:rgba(16,11,34,.82);
  border:1px solid var(--line);border-radius:14px;padding:7px 11px;display:flex;align-items:center;gap:10px}
.foe[hidden]{display:none}
.foe-name{font-size:13px;letter-spacing:1px;color:var(--bright);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.foe-bar{flex:1;height:9px;border-radius:999px;background:var(--panel2);overflow:hidden;border:1px solid var(--line)}
.foe-bar i{display:block;height:100%;background:linear-gradient(90deg,#c96a2a,var(--accent));
  border-radius:999px;transition:width .5s}

/* ---- kocka ---- */
/* THE DIE ROLLS OVER THE ROOM, not on a page of its own.
   It was a `.sheetover` — fixed, inset 0, an opaque page — so the picture, the people and the
   thing being attempted all disappeared for two seconds at the one moment they mattered, and came
   back afterwards. Now it is a layer inside the plate: the top half of the screen, which is where
   a table throws a die, with everything it is about still behind it. Translucent rather than
   opaque for the same reason — the room is the thing being rolled against. */
.plate .dicelayer{position:absolute;inset:0;z-index:12;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;background:rgba(10,6,22,.72);
  backdrop-filter:blur(2px);border-radius:inherit}
.plate .dicelayer[hidden]{display:none}
/* Smaller than the page-sized one was: it has a picture to sit on rather than a page to fill. */
.plate .dicelayer .die{width:clamp(96px,26vw,120px);height:clamp(96px,26vw,120px)}
.plate .dicelayer .die-text{font-size:15px;padding:0 14px;line-height:1.6}
/* THE DIE AND WHAT IT HAS TO BEAT, side by side and the same size. */
.dice .die-row{display:flex;align-items:center;justify-content:center;gap:clamp(14px,5vw,26px)}
.dice .die-need{width:clamp(96px,26vw,120px);height:clamp(96px,26vw,120px);display:flex;
  flex-direction:column;align-items:center;justify-content:center;border-radius:18px;
  border:2px solid var(--line);background:rgba(22,16,40,.92)}
.dice .die-need .lbl{font:12px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.dice .die-need .n{font:700 clamp(40px,12vw,52px)/1.05 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  color:var(--bright);margin-top:4px}
.dice.won .die-need,.dice.half .die-need{border-color:#3fbf73}
.dice.lost .die-need{border-color:var(--danger)}
/* ONE THING PER LINE: what you bring, the sum, and the word. */
.dice .die-text .d-stat{color:var(--ink)}
.dice .die-text .d-sum{color:var(--ink)}
.dice .die-text .d-word{margin-top:2px;font-weight:700;letter-spacing:.04em}
.dice.won .die-text .d-word{color:#5fd68f}
.dice.half .die-text .d-word{color:var(--accent)}
.dice.lost .die-text .d-word{color:var(--danger)}
.dice .die-eased{font-size:13px;color:var(--accent);text-align:center;padding:0 16px;line-height:1.4}
.dice .die-eased[hidden]{display:none}
/* AND THE ANSWER ABOVE IT ALL: a big check or a red cross, popping in when the number lands. */
.dice .die-mark{width:clamp(56px,16vw,72px);height:clamp(56px,16vw,72px)}
.dice .die-mark[hidden]{display:none}
.dice .die-mark svg{width:100%;height:100%;display:block}
.dice.won .die-mark{color:#5fd68f}
.dice.half .die-mark{color:var(--accent)}
.dice.lost .die-mark{color:var(--danger)}
.dice .die-mark.pop{animation:mark-pop .45s cubic-bezier(.2,1.6,.45,1) both}
@keyframes mark-pop{0%{transform:scale(.2);opacity:0}100%{transform:scale(1);opacity:1}}
@media(prefers-reduced-motion:reduce){.dice .die-mark.pop{animation:none}}
/* AND WHAT YOU CHOSE STAYS READABLE WHILE THE DIE TURNS. The chosen words sit on the plate at
   z 7 and the die's veil at 12, so they were blurred and dimmed under it — the one sentence that
   says what the die is FOR. Lifted over the veil while the die is up, without a scrim of its own:
   the veil is already the dark ground they need. */
.plate:has(.dicelayer:not([hidden])) .chose{z-index:13;background:none;pointer-events:none}
.dice .die{position:relative;width:130px;height:130px}
.dice .die svg{width:100%;height:100%;display:block}
.dice .die-num{position:absolute;inset:0;display:grid;place-items:center;
  font-family:ui-monospace,monospace;font-size:40px;color:var(--bright)}
.dice .die.roll svg{animation:tum .14s linear infinite}
@keyframes tum{from{transform:rotate(-9deg)}50%{transform:rotate(9deg)}to{transform:rotate(-9deg)}}
.dice .die-text{font-size:16px;color:var(--ink);text-align:center;padding:0 20px}
.dice .die-text b{color:var(--bright);font-weight:normal}
@media(prefers-reduced-motion:reduce){.dice .die.roll svg{animation:none}}
/* the world map still has a canvas — but it draws only markers, fog and the travel path (the
   controls), never terrain; the terrain is an API picture underneath. A room has no canvas. */
#scenePlate img{z-index:1}

/* the speaker's face beside the story */
/* the speaker's portrait was removed — the character now goes INTO the scene picture, and the
   text gets the whole box */

/* ---- the hero sheet: everything fits on screen, no scrolling ---- */
.herosheet{display:flex;flex-direction:column;justify-content:space-between;gap:8px;
  height:100%;min-height:0;max-width:420px;margin:0 auto}

/* The squares dictate the row height: three left, three right, and the hero between them
   stretches to match. That is why the row is NOT flex:1 — otherwise the squares would grow into
   rectangles on any tall screen. */
/* Columns of 1 : 2 : 1 give the hero a frame three squares tall and two wide — a ratio of
   0.63, exactly that of the generated hero picture (1024×1536). So the image fills the frame
   with no empty bands at the sides, and the squares stay square. */
.hero-row{flex:0 0 auto;display:grid;grid-template-columns:1fr 2fr 1fr;gap:8px;align-items:stretch}
.slots{display:flex;flex-direction:column;gap:8px}

.hero-figure{position:relative;border-radius:16px;overflow:hidden;border:1px solid var(--line);
  background:var(--panel2);min-height:0}
.hero-figure .nofig{position:absolute;inset:0;display:grid;place-items:center;padding:10px;
  text-align:center;font-size:13px;line-height:1.3;color:var(--muted)}
/* cover, not contain: the frame has the same ratio as the picture, so at most a pixel is cut,
   and nothing may be lost from the head (which is at the top) — hence object-position: top. */
.hero-figure img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:center top;opacity:0;transition:opacity .6s}
.hero-figure.has-art img{opacity:1}
.hero-figure.has-art .nofig{opacity:0}

.hero-name{text-align:center;line-height:1.25}
.hero-name b{display:block;font-size:17px;color:var(--bright);font-weight:normal;letter-spacing:1px}
.hero-name span{font-size:13px;color:var(--muted)}

.statbar{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;background:var(--panel);
  border:1px solid var(--line);border-radius:14px;padding:8px 6px;text-align:center}
.statbar b{display:block;font-family:ui-monospace,monospace;font-size:15px;color:var(--bright);font-weight:normal}
.statbar span{font-size:13px;letter-spacing:1px;color:var(--muted)}
.statbar .hp b{color:var(--good)}

/* the bag is always at the bottom, separated by a line — hero above, gear below */
.bagbox{flex:0 0 auto;border-top:1px solid var(--line);padding-top:8px}
.bagbox .label{text-align:left;margin:0 0 6px}
.grid.bag{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}

/* an item square — the same look beside the hero and in the bag */
.slot{position:relative;border-radius:12px;border:1px solid var(--line);background:var(--panel2);
  padding:0;overflow:hidden;display:grid;place-items:end center;aspect-ratio:1}
.slot.empty{border-style:dashed;opacity:.35}
.slot.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.slot-art{position:absolute;inset:0}
.slot .item-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .5s}
.slot .item-img.shown{opacity:1}
.slot-name{position:relative;z-index:1;width:100%;font-size:13px;line-height:1.15;color:var(--ink);
  background:linear-gradient(transparent,rgba(16,11,34,.92) 55%);padding:12px 3px 3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* The description card always sits in the same place, even when empty, so the sheet does not
   jump when an item is tapped. Empty, it is simply an instruction. */
.itemcard{flex:0 0 auto;display:flex;gap:10px;align-items:center;min-height:66px;
  background:var(--panel);border:1px solid var(--accent);border-radius:14px;padding:9px 11px}
.itemcard.hint{border-style:dashed;border-color:var(--line);justify-content:center;
  color:var(--muted);font-size:13px}
.itemcard.hint .itemcard-art{display:none}
/* A number has no picture, and a picture-shaped hole where one was never coming is worse than
   no box at all. */
.itemcard-art.none{display:none}
.itemcard-art{position:relative;flex:0 0 46px;width:46px;height:46px;border-radius:10px;
  overflow:hidden;border:1px solid var(--line);background:var(--panel2)}
.itemcard-art .item-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .5s}
.itemcard-art .item-img.shown{opacity:1}
.itemcard-name{color:var(--bright);font-size:15px;margin-bottom:2px}
/* what an item grants — only if it grants anything; otherwise the row does not exist */
.itemcard-bonus{display:flex;flex-wrap:wrap;gap:5px;margin:0 0 3px}
.itemcard-bonus:empty{display:none}
.itemcard-bonus i{font-style:normal;font-family:ui-monospace,monospace;font-size:13px;
  color:var(--good);border:1px solid var(--line);border-radius:999px;padding:1px 7px}
.itemcard-desc{font-size:13px;color:var(--muted);line-height:1.35}
.slot-bonus{position:absolute;top:3px;right:5px;z-index:2;font-size:13px;color:var(--good);
  text-shadow:0 1px 3px rgba(0,0,0,.9)}

/* On short screens the squares follow the height rather than the width, and every fixed band
   is tightened — otherwise the bag and the card fall below the "Close" button. */
/* Below ~700px of height the airy layout no longer fits: the label moves back to the side and
   the rows tighten up. Dense beats scrolling. */
@media (max-height:700px){
  .step.airy{justify-content:flex-start;gap:5px}
  .step.airy .pick{flex-direction:row;align-items:flex-start;gap:10px}
  .step.airy .pick .label{flex:0 0 58px;padding-top:12px;text-align:left;
    font-size:13px;letter-spacing:2px}
  .step.airy .chips{justify-content:flex-start}
  .step.airy .chip{min-height:40px;font-size:13.5px;padding:0 12px}
  .step.airy .card.desc{margin-top:auto}
}

@media (max-height:760px){
  .herosheet{max-width:min(420px,42vh);gap:6px}
  .hero-name b{font-size:15px}
  .hero-name span{font-size:13px}
  .statbar{padding:5px 6px;border-radius:12px}
  .statbar b{font-size:14px}
  .itemcard{min-height:54px;padding:7px 10px}
  .itemcard-art{flex-basis:38px;width:38px;height:38px}
  .itemcard-name{font-size:14px}
}
/* The hero sheet should fit without scrolling, and does on anything from an iPhone upwards;
   on older small phones scrolling is the last safety net, so nothing gets cut off. */
#sheet .body{overflow-y:auto}

/* ==== the hero sheet in an authored evening ====
   Three world slots instead of six worn things, so the row is two columns rather than three.
   Everything else — the squares, the tap-to-read card, the bag grid — is the layout that was
   already here. */

.hero-row.gear3{grid-template-columns:1fr 2.2fr}
.hero-row.gear3 .slots{display:flex;flex-direction:column;gap:6px;justify-content:center}
/* A slot says what it IS even when it is empty: a world has a Signature Tool whether or not
   anybody is carrying one yet. */
.slot .slotlabel{display:block;font-size:13px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);line-height:1.2}
.slot .slotname{display:block;font-size:13px;line-height:1.25;color:var(--ink)}
.slot.empty .slotname{color:var(--muted)}
.slot .qty{position:absolute;right:3px;bottom:2px;font-size:13px;font-style:normal;color:var(--accent)}
/* Carried, and honestly not usable here. Never drawn as if it were ready. */
.slot.inactive{opacity:.42}
.slot.inactive .slotname{text-decoration:line-through}
.slot.depleted{opacity:.42}

.herotabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.herotabs .chip{min-height:36px}
.statbar .core b{color:var(--accent)}

/* The adventure card's picture. Tall, because it is looked at while choosing — the same plate
   states as everywhere else: drawing / here / there will not be one. */
.advstep #advPlate{flex:1 1 auto;min-height:0;border-radius:14px;overflow:hidden;position:relative}
.advstep #advPlate img{width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s}
.advstep #advPlate img.shown{opacity:1}
.advstep{display:flex;flex-direction:column;gap:10px;min-height:0}

/* ==== WHO IS PLAYING ====
   Two to five people configured on one phone screen. The hard part is that each person needs
   four answers, and four rows of chips per person turns into a wall. So each person is a CARD
   with their own quiet frame, and inside it the rows are labelled — without the labels nobody
   can tell which row of chips is an age and which is a role. */

.v2players{display:flex;flex-direction:column;gap:10px;min-height:0}
.v2players[hidden]{display:none}
.roster{display:flex;flex-direction:column;gap:10px;min-height:0;overflow-y:auto;padding-right:2px}

.playerrow{position:relative;display:flex;gap:10px;align-items:flex-start;
  padding:10px 12px;border:1px solid var(--line);border-radius:14px;background:var(--panel2)}
.playerrow .avatar{width:44px;height:44px;flex:0 0 auto;display:grid;place-items:center;font-size:24px;
  border-radius:12px;background:var(--panel);border:1px solid var(--line);cursor:pointer;padding:0}
.playerfields{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.playerfields .pname{width:100%;min-height:40px;padding:8px 10px;border-radius:10px;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);font:inherit;font-size:15px}
.playerfields .pname::placeholder{color:var(--muted)}

/* A row of chips says what it is. Small, above, and out of the way — but there. */
.fieldrow{display:flex;flex-direction:column;gap:4px}
.fieldrow > .fieldlabel{font-size:13px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.chips.small{display:flex;flex-wrap:wrap;gap:4px}
.chips.small .chip{min-height:44px;padding:2px 10px;font-size:13px}
/* Pronouns are genuinely optional and there are five of them, so they get a quieter row.
   The 44px floor is for what you MUST hit to get on; this is what you may ignore. */
.chips.tiny{display:flex;flex-wrap:wrap;gap:4px}
.chips.tiny .chip{min-height:36px;padding:1px 9px;font-size:13px;opacity:.7}
.chips.tiny .chip.on{opacity:1}

/* Removing a person must be reachable and must never be the obvious tap. */
.dropplayer{position:absolute;top:6px;right:6px;width:32px;height:32px;border-radius:8px;
  border:0;background:transparent;color:var(--muted);font-size:20px;line-height:1;cursor:pointer}
.dropplayer:hover{color:var(--danger)}
.addplayer{min-height:44px;border-radius:12px;border:1px dashed var(--line);background:transparent;
  color:var(--muted);font:inherit;font-size:14px;cursor:pointer;flex:0 0 auto}
.addplayer:hover{color:var(--bright);border-color:var(--accent)}
.addplayer[hidden]{display:none}

/* ==== CHOOSING ==== */
.cardlist{display:flex;flex-direction:column;gap:8px;min-height:0;overflow-y:auto;flex:0 0 auto}
.bigcard{display:flex;flex-direction:column;gap:4px;text-align:left;min-height:44px;padding:12px 14px;
  border-radius:14px;border:1px solid var(--line);background:var(--panel2);color:var(--ink);
  font:inherit;cursor:pointer}
/* THE SAME SIZE AS THE REVEAL. This card and the page after it are the two things a family reads
   before the story starts, and the teaser was two sizes smaller than the sentence that follows it
   — which read as small print rather than as the invitation it is. */
.bigcard b{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(15px,3.9vw,16px);letter-spacing:.02em;
  color:var(--bright);line-height:1.25}
.bigcard span{font-size:clamp(15px,3.9vw,17px);line-height:1.45;opacity:.9}
.bigcard em{font-size:13px;font-style:normal;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
/* WHAT THE EVENING IS, under the pitch: what kind of story, how make-believe, how tense, and
   whether anybody gets hurt in it. A wrapping row of pills, upper-case with letter-spacing —
   which is the one register this project lets go to 12px, because the spacing gives the
   legibility back, and it also settles the authors writing "historical mystery" next to
   "Cinematic heroic fantasy".
   SPECIFICITY, NOT POSITION: `.bigcard span` is the pitch and sets 15-17px. Two classes beat one
   wherever this block happens to sit in the file — this file has twice been bitten by a rule that
   lost on order alone. */
.bigcard .tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
.bigcard .tags span{font-size:clamp(12px,3vw,12.5px);line-height:1.2;opacity:1;
  letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  border:1px solid var(--line);border-radius:999px;padding:3px 8px;background:var(--panel)}
.bigcard.on{border-color:var(--accent);background:var(--panel)}
/* An unfinished adventure is shown, and visibly is not an offer. */
.bigcard.dim{opacity:.45;cursor:default}
.bigcard:disabled{cursor:not-allowed}

/* ==== THE SHELF: worlds, and collections inside a world ====
   A grid of square tiles, two across on a phone and three when there is room. The tile carries
   its picture, its name and two numbers — finished, out of playable. It never scrolls the page:
   the grid itself scrolls if the shelf outgrows the screen. */
.shelfstep{display:flex;flex-direction:column;gap:10px;min-height:0}
.shelf{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;
  flex:1 1 auto;min-height:0;overflow-y:auto;align-content:start;padding-bottom:2px}
.tile{display:flex;flex-direction:column;gap:6px;padding:8px 8px 10px;min-height:44px;
  border-radius:16px;border:1px solid var(--line);background:var(--panel2);color:var(--ink);
  font:inherit;text-align:center;cursor:pointer;align-items:stretch}
.tile.on{border-color:var(--accent);background:var(--panel)}
.tile:disabled{cursor:not-allowed}
/* Dimmed, and visibly not an offer — the same treatment an unfinished adventure card gets. */
.tile.soon{opacity:.42}

/* The picture, and what stands in for it. The monogram sits underneath and is simply covered
   when the drawing arrives, so a tile is never an empty square and never a spinner that does
   not stop: if there will be no picture, this is the picture. */
.tileart{position:relative;display:block;width:100%;aspect-ratio:1/1;border-radius:12px;
  overflow:hidden;background:linear-gradient(160deg,hsl(var(--h) 42% 16%),hsl(var(--h) 38% 8%))}
.tileart b{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:34px;font-weight:400;color:var(--bright);opacity:.55}
.tileart img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.tilename{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:14px;line-height:1.2;color:var(--bright)}
/* THIRTEEN, NOT TWELVE. The 12px allowance is for an uppercase label with letter-spacing, where
   the spacing buys the legibility back; this is two digits and a slash — "1 / 1" — and a family
   reads it to decide which shelf to open. Caught by measuring the shelf at 360px, which is the
   narrowest screen this game is built for and the one where nothing else had been measured. */
.tilecount{font:13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.06em;color:var(--muted)}

@media (min-width:520px){ .shelf{grid-template-columns:repeat(3,1fr)} }
/* On a short screen the picture gives way first — the name and the count must stay readable. */
@media (max-height:640px){ .tileart b{font-size:26px} .tilename{font-size:13px} }

/* ==== MAKE IT YOURS ==== */
.moreopts{min-height:44px;border:0;background:transparent;color:var(--muted);font:inherit;
  text-decoration:underline;cursor:pointer;align-self:flex-start}
.advanced{display:flex;flex-direction:column;gap:10px}
.advanced[hidden]{display:none}
/* A value the youngest player rules out is shown as unavailable, never accepted and then
   silently undone: a control that takes a tap and reverses it teaches nobody anything. */
.chip.off{opacity:.3;text-decoration:line-through;cursor:not-allowed}

/* ==== READY ====
   A label and its value are two things, and they were running into one another. Grid, not
   flex: the labels line up in their own column and the values get whatever is left. */
.v2ready{display:flex;flex-direction:column;gap:12px;min-height:0;overflow-y:auto}
.summary{display:flex;flex-direction:column;gap:10px}
.sumrow{display:grid;grid-template-columns:minmax(72px,auto) 1fr;gap:12px;align-items:baseline}
.sumrow > span{font-size:13px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);
  padding-top:3px}
.sumrow > b{font-size:16px;line-height:1.3;color:var(--bright);font-weight:600}
.sumrow b em{font-style:normal;font-size:13px;color:var(--muted);white-space:nowrap}
.sumline{font-size:14px;line-height:1.45;color:var(--ink);opacity:.85;
  padding-left:calc(72px + 12px)}
.sumwarn{padding:10px 12px;border-radius:10px;border:1px solid var(--accent);
  background:rgba(232,165,74,.08);font-size:13px;line-height:1.4;color:var(--bright)}
.loadouts{margin-top:auto;padding-top:10px;border-top:1px solid var(--line)}

@media (max-height:700px){
  .sumrow{grid-template-columns:1fr;gap:2px}
  .sumline{padding-left:0}
  .playerfields{gap:6px}
}

/* An authored hero has no painted figure: the family chose their own face on the setup screen,
   and the world supplies the name of what they do. That pair IS the portrait here — putting a
   generated one over it would replace a child's choice with a picture nobody asked for. */
.hero-figure.figbadge{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:8px;padding:16px 12px;text-align:center}
.figbadge .figface{font-size:64px;line-height:1}
.figbadge .figrole{font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:18px;letter-spacing:.02em;color:var(--bright)}
.figbadge .figsay{font-size:13px;line-height:1.4;color:var(--muted);max-width:24ch}

/* A world gear slot is NOT one of the square item tiles: it is a labelled row that has to hold
   "Радионички безбедносни мантил" without spilling. So it drops the square aspect, sizes to its
   own text, and wraps. */
.slot.gearslot{aspect-ratio:auto;display:flex;flex-direction:column;justify-content:center;
  gap:3px;padding:8px 10px;text-align:left;min-height:56px}
.slot.gearslot .slotlabel{white-space:normal}
.slot.gearslot .slotname{white-space:normal;overflow-wrap:anywhere;font-size:13px;line-height:1.25}

/* The sheet must fit a phone. The gear column and the badge share one row that takes what it
   needs and no more; if a very long loadout still overflows, the sheet scrolls rather than
   letting the close button sit on top of the item card. */
.herosheet{overflow-y:auto}
.hero-row.gear3{flex:0 0 auto;align-items:stretch}
.hero-row.gear3 .slots{gap:6px}
.figbadge{min-height:0}
@media (max-height:760px){
  .figbadge .figface{font-size:44px}
  .figbadge .figrole{font-size:15px}
  .figbadge .figsay{display:none}
  .slot.gearslot{min-height:48px;padding:6px 8px}
}
/* AND ON A SHORT PHONE THE PICTURE IS WHAT GIVES WAY. Everything else on this sheet is a fact —
   three numbers, three slots, what is in the bag, and the card that explains whatever was
   tapped — and a fact cannot be made shorter. Measured at 393×640 the panel ran 72px past the
   bottom; the portrait gives those up and keeps its shape, because the frame it sits in is the
   ratio of the drawing and cropping it is what `object-fit:cover` is for. */
@media (max-height:700px){
  /* THE SQUARES SET THE ROW'S HEIGHT, not the picture — three of them stacked are taller than
     the frame beside them the moment the frame is capped. So the COLUMN narrows and they come
     down with it, and the picture follows the shape it has always had. */
  /* `.herosheet .hero-row.gear3`, NOT `.hero-row.gear3`: the sheet's own column rule is written
     further down this file at the same specificity, so a plain selector here loses on order and
     the measurement said exactly that — the squares stayed 87px through two attempts to shrink
     them. This project has been here before, one layout down. */
  .herosheet .hero-row.gear3{grid-template-columns:2.7fr 1fr}
  .herosheet .hero-figure{max-height:230px}
  .herosheet .statbar{padding:5px 6px}
  .herosheet .itemcard{min-height:52px;padding:6px 9px}
  .herosheet .bagbox{padding-top:5px}
  .herosheet .bagbox .label{margin:0 0 4px}
  /* A narrower panel fits three of the same squares where a wider one fits four, and the row is
     as tall as one square — so on a short phone the squares come down too and the fourth fits. */
  .herosheet .bagbox .grid.bag{grid-template-columns:repeat(auto-fill,minmax(58px,1fr))}
}

/* ==== TONIGHT, YOU ARE … ====
   The last screen before the story, and the only one that asks nothing. A face, three numbers
   with the sentence that says what each one MEANS here, what you are wearing, and one honest
   line about which parts of the evening really happened. */
/* TWO HALVES, AND NEITHER SCROLLS. It used to be one page with `overflow-y:auto`, which meant
   the third piece of equipment lived below the green button where nobody found it. Each half is
   now a column of fixed rows with one thing that gives way — the portrait on the first, nothing
   on the second because three short bands always fit. */
.youstep{display:flex;flex-direction:column;gap:8px;min-height:0;overflow:hidden}
.youpage{flex:1 1 auto;display:flex;flex-direction:column;gap:10px;min-height:0}
/* Three bands, evenly spread — the slack falls BETWEEN them rather than all of it under the
   last one. */
.youpage[data-page="2"]{justify-content:space-evenly}
.youpage[hidden]{display:none}
.youstep .sheetnav{flex:0 0 auto;min-height:38px}
/* WHAT THE FAMILY READS SCROLLS, AND SAYS SO. Same shape as the story box: the fade and the one
   line under it go when they reach the end.
   WHO TAKES THE SLACK CHANGED WHEN THE HONEST NOTE LEFT THIS PAGE. It used to be the words: the
   note had no fixed length and the picture kept a 200px floor, so a long note cost picture and
   never words. With the note gone the two blocks that are left are short enough to fit on any
   phone this game supports — so the leftover height goes to the PORTRAIT, which is the whole
   reason this page exists.
   BOTH STILL GIVE WAY, in that order: the picture grows into slack and stops at its floor, and
   only then does the reading box shrink and scroll. A protagonist described in five lines must
   never push the dots off a page that clips. */
.youread{flex:0 1 auto;min-height:0;overflow-y:auto;padding-right:2px;
  display:flex;flex-direction:column;gap:10px;justify-content:flex-start}
.youread.more{mask-image:linear-gradient(180deg,#000 86%,transparent);
  -webkit-mask-image:linear-gradient(180deg,#000 86%,transparent)}
.youstep #youPlate{flex:1 1 auto;min-height:220px;border-radius:14px;overflow:hidden;position:relative}
.youstep #youPlate img{width:100%;height:100%;object-fit:cover;object-position:50% 22%;opacity:0;transition:opacity .4s}
.youstep #youPlate img.shown{opacity:1}
/* THE THREE PAGES SPEAK IN ONE VOICE. This line was 20-24px bold while the headings on the two
   pages after it — ABOUT YOU, GOOD AT — are 15-16px, so the first page read as a different
   screen from the other two. Same size and weight as those, in the accent colour, and the
   paragraph under it stays ink: exactly the shape page 2 has. */
.youtext b{display:block;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(15px,3.9vw,16px);
  font-weight:normal;letter-spacing:.02em;line-height:1.35;color:var(--accent)}
/* WHO, THEN WHEN AND WHERE — two answers, not one paragraph in two colours. They used to sit
   7px apart, which on a page with a third block under them read as one run of text; with the
   third block gone they are the only two things there, and the space between them is what says
   they answer different questions. */
.youtext p{margin:13px 0 0;font-size:clamp(15px,3.9vw,17px);line-height:1.45;color:var(--ink)}

/* WHO HE FEELS LIKE. Paragraphs, at reading size — this is the one block on the way in that a
   family actually reads rather than scans, so it is the last place to save space. It is also the
   only thing on either page allowed to scroll, because a good portrayal has no fixed length. */
/* A heading before the words, so the page announces itself rather than starting mid-thought. */
.pagehead{flex:0 0 auto;margin:0;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(15px,3.9vw,16px);
  font-weight:normal;letter-spacing:.1em;text-transform:uppercase;color:var(--accent)}
/* IT SCROLLS, SO IT STARTS AT THE TOP. `justify-content:center` on a scrolling flex column
   centres content that is taller than the box — and flexbox clips the overflow at the START
   edge, where no amount of scrolling reaches it. The first paragraph of the one page in this
   game whose whole job is to make somebody care about a character was simply cut off. */
.portrayal{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:10px;
  padding-right:2px;justify-content:flex-start}
/* AND IT SAYS SO. A box that scrolls with no sign that it scrolls is a box whose bottom half
   nobody reads. The fade goes when they reach the end. */
.portrayal.more{mask-image:linear-gradient(180deg,#000 84%,transparent);
  -webkit-mask-image:linear-gradient(180deg,#000 84%,transparent)}
.morehint{flex:0 0 auto;text-align:center;font-size:clamp(13px,3.3vw,14px);color:var(--accent);
  opacity:.9;padding-top:2px}
.morehint[hidden]{display:none}
.portrayal p{margin:0;font-size:clamp(14.5px,3.8vw,16px);line-height:1.5;color:var(--ink)}
/* THE HONEST NOTE lives in the BOOK now — see `web/css/book.css`. It was one of three things on
   the way in, at reading size, where it cost the portrait a third of the page that has to make
   somebody want to BE this person; and it was tried on the hero sheet, where four lines of prose
   put that panel into scrolling at two sizes where it fitted before. A colophon wants a page
   that is allowed to be long, and the keepsake is the only one this game has. */
/* THREE SECTIONS, AND THEY LOOK LIKE THREE. Labels the colour of the background with nothing
   between them read as one long list; a band with its own panel, its own heading and its own
   edge reads as an answer to a question. */
.band{flex:0 0 auto;background:var(--panel2);border:1px solid var(--line);border-radius:14px;
  padding:9px 11px 11px}
/* A HEADING HAS TO WIN. These were the same weight and nearly the same size as the text under
   them, so three sections read as one list with decoration in it. */
.band h3{margin:0 0 9px;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(15px,3.9vw,16px);
  font-weight:normal;letter-spacing:.1em;text-transform:uppercase;color:var(--accent)}

.youstats{display:flex;flex-direction:column;gap:7px}
/* The number is read first, then the name, then what it is for — so the column of numbers lines
   up and the eye can run down it. */
.stat{display:grid;grid-template-columns:auto 1fr;gap:1px 10px;align-items:baseline}
.stat i{grid-row:1;grid-column:1;font-style:normal;
  font:clamp(17px,4.4vw,19px)/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--accent)}
.stat b{grid-row:1;grid-column:2;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(16px,4.1vw,17px);color:var(--bright)}
/* ONE LINE, ALWAYS. A meaning that folds onto a second line stops reading as a label and starts
   competing with the name above it, so it is clipped rather than wrapped — and the content is
   written short enough that it never comes to that. */
.stat span{grid-row:2;grid-column:2;font-size:clamp(13px,3.4vw,14px);line-height:1.35;
  color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* A ROW WITH ITS OWN PICTURE. The icon is the same drawing the sheet uses, so a thing looks like
   itself everywhere it appears — and a row of pictures is read far faster than a row of names. */
.yougear{display:flex;flex-direction:column;gap:9px}
.gear{display:grid;grid-template-columns:auto 1fr;gap:10px;align-items:start}
.gearart{width:46px;height:46px;border-radius:10px;border:1px solid var(--line);
  background:var(--panel);overflow:hidden;display:block;position:relative}
.gearart img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .4s}
.gearart img.shown{opacity:1}
.gear b{display:block;font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.gear span{display:block;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(15px,3.9vw,16px);color:var(--bright)}
.gear em{display:block;font-style:normal;font-size:clamp(13px,3.4vw,14px);line-height:1.4;color:var(--muted)}

/* The protagonist's sheet: the same three-number block as the reveal, so the two screens read
   as the same person rather than two summaries of them. */
.herosheet .sheetstats{gap:8px;margin:6px 0 2px}
.herosheet .slots.wide{display:flex;flex-direction:column;gap:6px}
.herosheet .slots.wide .slot.gearslot{width:100%;min-height:44px;height:auto;padding:8px 10px;
  flex-direction:row;align-items:baseline;gap:10px;text-align:left}
/* A FLEX ITEM WITH aspect-ratio:1 IS AS WIDE AS ITS LABEL, and then as TALL as it is wide.
   This line was written for the old six-slot sheet and it caught the protagonist's bag as it
   went past: "Кука и уже од свиленог челика" made its own square 235px across, two fitted on a
   row, and the panel whose whole promise is that it fits overflowed by 203px at 393×740 and 261
   at 393×640. A grid sizes from the ROOM, not from the words. */
.herosheet .grid.bag{display:grid;grid-template-columns:repeat(auto-fill,minmax(68px,1fr));gap:6px}

/* The line under the green button says why it is waiting. When what it is saying is a failure
   rather than a rule, it stops being quiet. */
.hint.center.bad{color:var(--danger)}

/* What tonight narrowed. The settings were answered before the adventure was chosen, so this is
   where a family learns one of their answers did not survive it. Said once, in the accent, so it
   is noticed and not alarming. */
/* WHAT IS IN A POCKET IS A FOOTNOTE. One row, icons with their names beneath, along the bottom —
   a watch and a stick of chalk do not deserve the weight of the three things he is wearing. */
/* WHAT IS IN A POCKET IS STILL A SECTION, just a smaller one — pinned to the floor it read as
   a footer bolted on, and the page had a lake of nothing in the middle of it. */
.bagstrip{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap}
.bagthing{display:flex;align-items:center;gap:9px}
.bagthing .gearart{width:40px;height:40px;flex:0 0 40px}
.bagthing em{font-style:normal;font-family:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Cascadia Mono','Segoe UI Mono','Liberation Mono',monospace;font-size:clamp(14px,3.6vw,15px);
  line-height:1.25;color:var(--bright)}

/* THE PROTAGONIST'S SHEET. Three slots instead of six, so the row is picture-then-slots rather
   than slots on both sides. Same ratio as before: the figure column is 2fr and the slot column
   is 1fr, which makes the figure exactly as tall as three squares — 0.63, the shape of the
   generated portrait, so it fills its frame with no empty bands. */
.hero-row.gear3{grid-template-columns:2fr 1fr}
/* THE SQUARES DICTATE THE HEIGHT, as they always did. Left to stretch inside a column flexbox
   they became tall rectangles and the picture grew to fill a phone; pinned to their own size,
   the row is three squares tall and the figure — 2fr wide against their 1fr — lands at 0.63,
   the shape of the generated portrait. */
.hero-row.gear3 .slots.right{justify-content:flex-start}
.hero-row.gear3 .slots.right .slot{flex:0 0 auto;width:100%}
.statbar.three{grid-template-columns:repeat(3,1fr)}
.statbar button{border:0;background:transparent;color:inherit;font:inherit;padding:0;cursor:pointer}
.statbar button b{color:var(--accent)}
/* A carried thing has a name and no picture, so it needs the whole square to be readable. */
.grid.bag .slot .slot-name{white-space:normal;font-size:13px;line-height:1.2;
  padding:4px 3px;background:none;place-self:center;text-align:center}
.grid.bag .slot{place-items:center}
.grid.bag .slot .qty{position:absolute;top:3px;right:5px;font-style:normal;
  font:12px/1 ui-monospace,monospace;color:var(--accent)}

/* With no picture yet, the name has the whole square rather than sitting in a gradient at the
   bottom of an empty box. When the picture arrives it slides back down over it. */
.slots.right .slot .slot-name{white-space:normal;font-size:13px;line-height:1.2;
  padding:6px 4px;background:none;place-self:center;text-align:center}
.slots.right .slot{place-items:center}
.slot:has(.item-img.shown) .slot-name{place-self:end center;text-align:center;
  background:linear-gradient(transparent,rgba(16,11,34,.92) 55%);padding:12px 3px 4px}

/* "This one will probably want a die." Same chip as the DC that appears after a ruling, so the
   two read as the same thing said earlier and then precisely. */
/* THE DIE SAYS IT BY BEING A DIE. It used to read "🎲 a roll" in a small pill — the word is the
   picture said twice, in a row where every character costs the label beside it space. Bigger,
   and with no chip around it: it is a mark on the button, not a second label. */
.act .dc.die{opacity:.85;border:none;background:none;padding:0 2px;
  font-size:clamp(19px,5vw,23px);line-height:1}
/* SOMETHING IN HAND ALREADY MAKES THIS ONE EASIER. The accent, because it is good news — the
   die marker beside it is neutral, and the two must not read as the same kind of mark. */
.act .dc.eased{opacity:.9;color:var(--accent);letter-spacing:.04em}
/* NOT A RUNG. It sits above the ladder and says what was left behind, so it must not read as
   one of the author's three hints. */
.hint.easier{border-left:2px solid var(--accent);padding-left:.6em;opacity:.92}
.hint.easier p{margin:.2em 0}

/* ==== THE SHEET IN TWO PAGES ====
   Neither page scrolls. Every row is a fixed height and the face takes whatever is left, which
   is what makes "no scrolling" a property of the layout rather than a hope about content. */

/* With a picture behind it a name may take two lines rather than ending in an ellipsis. */
.grid.bag .slot .slot-name{white-space:normal;font-size:13px;line-height:1.15}

/* Dots and a counter — the same paging language the story uses. `flex:0 0 auto` plus its own
   top padding is what keeps it OFF the text above it: as a flex item with no basis it was being
   squeezed to nothing and then drawn over the last paragraph. */
.sheetnav{flex:0 0 auto;display:flex;align-items:center;justify-content:center;gap:8px;
  margin-top:6px;padding-top:8px;border-top:1px solid var(--line)}
.sheetnav .dot{width:26px;height:26px;padding:0;border-radius:999px;border:1px solid var(--line);
  background:transparent;cursor:pointer;position:relative}
.sheetnav .dot::after{content:'';position:absolute;inset:8px;border-radius:999px;background:var(--muted)}
.sheetnav .dot.on{border-color:var(--accent)}
.sheetnav .dot.on::after{background:var(--accent)}
/* 13px, like everything else a family reads. It was 12: the page counter is small and dim and
   easy to leave under the floor, and it is also the one thing on the page that says how much is
   left — which is exactly the thing somebody squints at. */
.sheetcount{font:13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted)}
/* What he knows, in his own terms — three short lines, not a paragraph. On the way in only. */
.knows{flex:0 0 auto;display:flex;flex-direction:column;gap:3px}
.knows span{font-size:13px;line-height:1.35;color:var(--muted);padding-left:12px;position:relative}
.knows span::before{content:'·';position:absolute;left:2px;color:var(--accent)}

/* WITH THE DICE GONE THE BANNER TAKES ITS PLACE. A heading of three words per line inside a box
   sized for two is a heading nobody reads — and on these pages there is nothing for the dice to
   fill in any more. */
/* WITH NO DICE IN THE HEADER the banner has the width, always. It filled in one thing — the
   age — and sat in every header for the whole of setup as a mystery. */
.setup .banner{padding:8px 16px}
.setup .banner b{font-size:clamp(15px,4vw,17px);letter-spacing:1.5px}
.setup .banner span{font-size:clamp(12px,3.1vw,13px);letter-spacing:1.2px;
  text-transform:uppercase;line-height:1.25}

/* ==== ABOUT YOU: a framed page ====
   This page is nothing but words, and words on a bare screen read as a wall of them. So it gets
   a frame of its own, a centred heading, and the one piece of ornament in the whole game — a
   rule with a diamond in it. The ornament is not decoration for its own sake: it says "this
   part is about a person" rather than "this part is a form", which is exactly the difference
   this page has to carry. */
.aboutpage{padding:2px 0}
.aboutframe{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;
  background:linear-gradient(180deg,var(--panel2),rgba(14,10,30,0));
  border:1px solid var(--line);border-radius:18px;padding:14px 16px 16px;
  box-shadow:inset 0 1px 0 rgba(255,217,168,.06)}

/* The rule and the diamond. Thin, low-contrast, and the same accent as every heading — an
   ornament that shouts is worse than none. */
.flourish{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:0 4px 6px}
.flourish i{flex:1;height:1px;background:linear-gradient(90deg,transparent,var(--line) 40%,var(--line) 60%,transparent)}
.flourish b{font-size:13px;line-height:1;color:var(--accent);opacity:.75;font-weight:normal}

/* The heading is a FIXED ROW, not something the scrolling text may slide under. Without the
   basis it was treated as shrinkable, the paragraphs took its height, and the first line of the
   portrayal was drawn straight through the words ABOUT YOU. */
.aboutpage .pagehead{flex:0 0 auto;text-align:center;margin:0 0 4px;padding-bottom:2px}
/* NOT CENTRED. This rule is the one that actually applied, and it put `justify-content:center`
   back on a box that scrolls — so the first paragraph sat 87 pixels above the top of its own
   container, where scrolling cannot reach. On the one page whose whole job is to make somebody
   care about a character, the first thing they read was the middle of a sentence. */
.aboutpage .portrayal{flex:1 1 auto;min-height:0;justify-content:flex-start;padding:6px 2px 0}
.aboutpage .portrayal p{text-align:left}

/* THE ADVENTURE MAP, laid over the picture like the hint panel — a state of this screen, not
   another page. The canvas fills the plate: the painted map is the background image, and every
   pin, label and line is drawn on top of it, which is why the picture can never claim a place
   is somewhere it is not.

   The plate has a fixed height and the map is a canvas, so this cannot scroll and cannot change
   the height the paginator measures. */
.mapover{position:absolute;inset:0;z-index:5;width:100%;height:100%;border-radius:14px;
  /* OPAQUE. At a third of an alpha the plate's own "no picture" state showed through it — its
     name for the room, its decoration and the adventure title behind, all under the map's own
     labels. Two pictures at once reads as a broken screen, not as depth. The canvas paints its
     own ground as well, for the same reason. */
  background:var(--bg);cursor:pointer;touch-action:manipulation}
.mapover[hidden]{display:none}
/* Why a door is shut, or that a tap landed on nothing. One line, over the bottom of the map,
   said in the fiction — never an unlock expression. */
.maptip{position:absolute;left:10px;right:10px;bottom:10px;z-index:6;
  background:rgba(11,7,22,.9);border:1px solid var(--line);border-radius:10px;
  padding:8px 10px;color:var(--ink);font-size:clamp(13px,3.4vw,14px);line-height:1.35}
.maptip[hidden]{display:none}
/* THE WAY OUT OF THE MAP. Top right, where a close sits on every other panel on this screen —
   the hint lamp's own corner, and the lamp is never up while the map is, so they cannot collide.
   Above the map's z-index, or the canvas that fills the plate would swallow the tap. */
.mapclose{position:absolute;right:10px;top:10px;z-index:7;width:44px;height:44px;
  border-radius:999px;border:1px solid var(--line);background:rgba(11,7,22,.9);
  color:var(--ink);font-size:22px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.mapclose[hidden]{display:none}
.mapclose:active{background:#3a2a18;border-color:var(--accent)}

/* THE PAGE TURN IS NOT A COMMITMENT. Same button, same place — but while there is more to read
   it is quiet, and only the last press (the one that puts the story away and asks for an action)
   looks like a decision. */
#doneBtn.paging{background:var(--panel);border-color:var(--line);color:var(--muted);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.04em}
#doneBtn.paging:active{background:var(--panel2);border-color:var(--accent);color:var(--ink)}

/* ---------- WHO AND WHAT IS ON THE PICTURE ----------
 *
 * The plate has always been the one part of this screen that says nothing while a decision is
 * being made: the story is put away to make room for the choices, and what is left above them is
 * a photograph. These three overlays are what it says instead, and each of them answers a
 * question a family was actually asking out loud.
 *
 * They live over the picture rather than beside it because there is no beside — the screen is a
 * phone, every band is already spoken for, and the picture is the one surface with room on it.
 * And they follow the same rule as the lamp and the map: ONLY while an action is being chosen.
 * While the story is being read, the picture is the picture.
 */

/* WHO IS HERE. Top left, under the name of the place, because top right is the lamp. A column
   rather than a row: faces are round and read down the edge of a picture the way a cast list
   does, and a row would fight the place name for the same line. */
.plate .cast{position:absolute;left:8px;top:40px;z-index:6;display:flex;flex-direction:column;
  gap:6px;max-height:calc(100% - 96px);overflow-y:auto;scrollbar-width:none}
.plate .cast::-webkit-scrollbar{display:none}
.plate .cast[hidden]{display:none}
.plate .cast button{width:clamp(44px,12vw,54px);height:clamp(44px,12vw,54px);padding:0;
  border-radius:50%;border:2px solid rgba(232,165,74,.55);background:rgba(11,7,22,.72);
  overflow:hidden;cursor:pointer;display:flex;align-items:center;justify-content:center;
  color:var(--bright);font-weight:600;font-size:15px;line-height:1;
  box-shadow:0 2px 10px rgba(0,0,0,.5);flex:0 0 auto}
.plate .cast button:active{border-color:var(--bright);transform:scale(.94)}
/* THE ONE STANDING AGAINST THEM: a red ring, hearts underneath, grey and struck through when over.
   `rival`, NOT `foe`: `.foe` is the free-form game's combat bar (`position:absolute`, pinned to the
   bottom), and the first version shared the word — the button left its own wrapper, slid out of the
   strip and was clipped. The fourth time a list rule has reached into the plate by sharing a name. */
.plate .cast .rival-wrap{display:flex;flex-direction:column;align-items:center;gap:2px;flex:0 0 auto}
.plate .cast button.rival{border-color:var(--danger);box-shadow:0 0 0 1px rgba(224,85,69,.35),0 2px 10px rgba(0,0,0,.5)}
.plate .cast .rival-wrap.over button.rival{border-color:rgba(143,127,184,.6);position:relative}
.plate .cast .rival-wrap.over button.rival img{filter:grayscale(1) brightness(.75)}
.plate .cast .rival-wrap.over button.rival::after{content:'';position:absolute;left:8%;right:8%;top:50%;height:3px;
  background:var(--danger);transform:rotate(-38deg);border-radius:2px;box-shadow:0 0 3px rgba(0,0,0,.6)}
.plate .cast .hearts,.look .hearts{display:flex;gap:1px;font-size:12px;line-height:1;color:var(--danger);
  text-shadow:0 1px 2px rgba(0,0,0,.8)}
.plate .cast .hearts i,.look .hearts i{font-style:normal}
.plate .cast .hearts i:not(.on),.look .hearts i:not(.on){color:rgba(216,207,232,.45)}
.look .hearts.big{font-size:20px;gap:4px}
/* THE PLATE'S OWN RULE HAS TO BE OVERRIDDEN, and this is the third time in this file. Every
   `img` inside `.plate` is absolutely positioned at `inset:0` and starts invisible — that is the
   cross-fade the room's picture arrives with, and its class is added by the plate's own loader.
   A picture written by hand never gets it, so it sits full-plate and invisible instead of in a
   44px circle. */
.plate .cast button img{position:static;inset:auto;width:100%;height:100%;opacity:1;
  object-fit:cover;border-radius:50%}

/* AND WHAT IS HERE. Along the bottom left, small and square, because a thing is a thing and a
   person is a face — making them the same shape would be saying they are the same kind of thing
   to tap. Each row scrolls sideways rather than wrapping: the picture is not a list. */
/* AND IT SPANS THE PLATE, so the grid inside it has something to be bounded BY.
 * A fixed cap was wrong twice over: 300px of icons fits inside a tall phone's plate and hangs
 * over the top of a short one's — measured at 640, a bag of fourteen started 45px above the
 * picture and crossed the room's name. The lanes still sit on the floor of it
 * (`justify-content:flex-end`); what changed is that "as tall as it needs" now means "as tall as
 * the picture allows". Nothing here takes a tap except the keys and the lists themselves — the
 * block is the size of the picture now, and a transparent sheet over a picture that swallows
 * taps is the oldest bug in this file's family. */
.plate .props{position:absolute;left:8px;top:46px;bottom:8px;right:8px;z-index:6;
  justify-content:flex-end;pointer-events:none;
  /* THREE ROWS, BOTTOM-UP. The keys used to be a column with one shared lane beside them, and a
     family tapping the working key watched the row under the EYE change. Each list now opens
     beside its own key, so which list belongs to which key is a fact of the layout.
     The room's own row is the LAST of the three, nearest the hand and nearest the picture it is
     about; what you brought stacks above it. */
  display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding-bottom:2px;
  /* AND ROOM ABOVE FOR A COUNT. `overflow-x:auto` computes `overflow-y:auto`, so this lane clips
     everything above its padding box — and a badge hangs 4px off the top of the key it counts.
     Measured: the topmost key's badge was sliced 3px along the top, and scrolling could not
     reach it either, because scrollable overflow does not extend above the block start. Padding
     rather than `overflow:visible`: the strips inside must go on clipping as they scroll. */
  padding-top:6px}
.plate .props::-webkit-scrollbar{display:none}
.plate .props[hidden]{display:none}
.plate .props button{width:44px;height:44px;flex:0 0 auto;padding:0;border-radius:10px;
  border:1px solid rgba(232,165,74,.4);background:rgba(11,7,22,.72);overflow:hidden;
  cursor:pointer;display:flex;align-items:center;justify-content:center;
  color:var(--muted);font-size:17px;box-shadow:0 2px 8px rgba(0,0,0,.45)}
.plate .props button:active{border-color:var(--bright);transform:scale(.94)}
.plate .props button img{position:static;inset:auto;width:100%;height:100%;opacity:1;
  object-fit:cover;border-radius:9px}
/* WHAT A COUNTED THING HAS LEFT (`charges.js`): a corner number while there is more than one to
   count, and a used-up thing dimmed but still there — it is still in the bag, it just does not
   work. Inside the square, so the square's own `overflow:hidden` keeps it. */
.plate .props button{position:relative}
.plate .props button .uses{position:absolute;right:2px;bottom:1px;min-width:14px;padding:0 3px;
  border-radius:7px;background:rgba(11,7,22,.85);color:var(--bright);
  font:600 12px/14px ui-monospace,monospace;text-align:center;pointer-events:none}
.plate .props button.spent img{opacity:.35;filter:grayscale(1)}
/* AND THE SQUARE ITSELF SAYS IT IS UNOPENED — a green frame instead of the bronze one, so the count
   on the key and the squares behind it say the same thing. Only the squares in the three lanes,
   never the keys; and the selected ring (`.on`, below in the file) still wins, by specificity. */
.plate .props #propList button.unread,.plate .props #bagStrip button.unread,
.plate .props #spellStrip button.unread{border:2px solid var(--go)}
.plate .props #propList button.unread.on,.plate .props #bagStrip button.unread.on,
.plate .props #spellStrip button.unread.on{border-color:var(--bright)}
.plate .props button.spent .uses{color:var(--muted)}
/* THE THING A HIDDEN PLACE SHOWS ITS SIGN ON wears the ring the dots under the doors key wear —
   one picture for "there is more here" and "look at this". It breathes slowly, so it is seen and
   not mistaken for a counter; gold where the place has to be found. */
.plate .props #propList button.secret::after{content:"";position:absolute;left:4px;top:4px;z-index:2;
  width:14px;height:14px;border-radius:50%;border:2px solid var(--bright);background:rgba(11,7,22,.7);
  box-shadow:0 0 6px rgba(255,217,168,.8);pointer-events:none;animation:sign-breathe 2.4s ease-in-out infinite}
.plate .props #propList button.secret.must::after{border-color:var(--accent);box-shadow:0 0 8px rgba(232,165,74,.95)}
.plate .props .propseye.secret::after{content:"";position:absolute;left:-3px;bottom:-3px;
  width:10px;height:10px;border-radius:50%;border:2px solid var(--accent);background:var(--bg);
  box-shadow:0 0 6px rgba(232,165,74,.9);pointer-events:none;animation:sign-breathe 2.4s ease-in-out infinite}
@keyframes sign-breathe{50%{opacity:.45}}
@media (prefers-reduced-motion:reduce){
  .plate .props #propList button.secret::after,.plate .props .propseye.secret::after{animation:none}
}

/* THE EYE THAT HOLDS THE ROW, and the row folded behind it.
   Six squares stood in front of the picture all evening whether anybody wanted them or not. The
   eye is one target always in the same place; the things open to its RIGHT and fold back on the
   same tap, and the eye never scrolls away with them because it is outside the scrolling half. */

/* THE TWO KEYS, STACKED, AND THE LANE IS SHARED. The bag used to hang in the top-right corner
   and open a column of its own down the right-hand side — a second row of squares over the same
   picture, which is the thing the eye was introduced to stop. Your things and the room's things
   are one question asked twice, so they get one strip: the bag above the eye, both opening to
   the eye's right. "Only one at a time" is then a fact of the layout rather than a rule. */
.plate .props .lane{display:flex;align-items:center;gap:6px;max-width:100%;min-width:0;
  pointer-events:auto}
/* AND A ROW WITH NOTHING IN IT TAKES NO HEIGHT. The working key and the bag key hide themselves
   when there is nothing to show, and a lane left standing around a hidden button is 44px of the
   picture spent on nothing. */
.plate .props .lane:not(:has(> button:not([hidden]))){display:none}
.plate .props .propseye{flex:0 0 auto;border-color:rgba(232,165,74,.7);
  color:var(--bright);font-size:19px}
.plate .props .propseye.on{background:#2a1a08;border-color:var(--accent)}
/* AND ITS COUNT, the same badge the bag has carried since it moved onto the picture. The eye
   needs `position:relative` of its own: `.plate .props button` does not set one, so the badge
   would have hung off the plate instead of off the eye. */
.plate .props .propseye{position:relative;overflow:visible}
/* AND THE SAME FOR ALL THREE KEYS, because the row's own rule was beating theirs. The thumbnails
   in the strip need `overflow:hidden` to keep their rounded corners, and that is written as
   `.plate .props button` — (0,2,1), which beats `.plate .bag` at (0,2,0). So the bag's own
   `overflow:visible`, written for exactly this, never applied once, and its count was cut along
   the top for as long as it has been on the picture. The eye and the working key escaped only by
   having three classes of their own. One rule for the keys, and it wins by specificity rather
   than by sitting further down the file — the trap this file has already been caught by twice. */
.plate .props .lane > button{overflow:visible}
/* THE THREE KEYS COUNT WHAT IS STILL UNOPENED behind them, in green — not how much is there. Green
   because red already means danger here (the miss, the foe's ring); this only means "new".
   See `unread` in location.js. */
.plate .props .propseye .badge{position:absolute;top:-4px;right:-2px;background:var(--go);
  color:#1a2a08;box-shadow:0 0 0 2px var(--bg);border-radius:999px;min-width:17px;height:17px;
  font:700 11px/17px ui-monospace,monospace;text-align:center;padding:0 3px}
.plate .props .propseye .badge[hidden]{display:none}
/* A GRID OVER THE PICTURE, NOT A ROW ALONG IT.
 *
 * It was one scrolling row per key, 44px squares, and a family could not make out what any of
 * them were — a bag of fourteen things was fourteen thumbnails dragged past the eye one at a
 * time. Opened now, a list takes the whole of what the three keys stand beside: everything to
 * the right of the key column, and the full height of all three lanes. FOUR ACROSS, measured —
 * 298px of width at 393 gives squares of 68px, half again the size they were.
 *
 * NO SCROLL, because a thing you have to drag to is a thing you did not know was there. It grows
 * UPWARD into the picture as the bag fills, and only a list long enough to reach the top of the
 * plate scrolls at all.
 *
 * STILL ANCHORED TO ITS OWN KEY. "Folded is width, not display" was how this said the list and
 * the key beside it are one thing; a grid cannot fold by width, so it is revealed left-to-right
 * by `clip-path` instead — the same movement, out from behind the key it belongs to. */
.plate .props .proplist{position:absolute;left:50px;right:0;bottom:0;
  /* CONTENT STARTS AT THE TOP OF THE BOX, and the BOX is what sits on the floor. Written as
     `align-content:end` it looked identical until the bag filled: overflow then went off the TOP
     of the box, where scrolling cannot reach it — scrollable overflow does not extend above the
     block start, which is the same trap the key badges hit one layer up. Measured at 640 with
     fourteen things: a 205px box reporting no overflow and a row of icons nobody could get to.
     The box is `height:auto` capped at the picture, so while everything fits it ends where the
     lanes do; past that it fills the picture and scrolls the ordinary way. */
  /* AND IT IS CENTRED ON THE MIDDLE KEY. Anchored to the floor, one row sat low and two rows
     sat high, so the block jumped as a bag filled. The box keeps a floor of the three keys'
     own height, so whatever is in it is centred on the middle one — the same gap above and
     below whether it holds one row or two — and past two rows the box grows upward from the
     same floor and the centre goes with it.
     `safe center` and not `center`: centred content that overflows spills off BOTH ends and the
     top one cannot be scrolled to, which is the trap this grid already hit once with `end`.
     `safe` falls back to `start` exactly there. */
  display:grid;grid-template-columns:repeat(4,1fr);gap:8px;align-content:safe center;
  min-height:calc(3 * 44px + 2 * 6px);
  max-height:100%;overflow-y:auto;scrollbar-width:none;
  clip-path:inset(0 100% 0 0);opacity:0;pointer-events:none;
  transition:clip-path .22s ease,opacity .16s ease}
.plate .props .proplist::-webkit-scrollbar{display:none}
/* AND WHAT IS IN IT IS A SQUARE OF THE GRID. The 44px fixed square was written for a row that
   scrolled; here the column width decides the size and the square just stays square. */
.plate .props .proplist > button{width:auto;height:auto;aspect-ratio:1;flex:initial}
/* A LIST LONGER THAN THE PICTURE SAYS SO, the way everything else on this screen does. */
.plate .props .proplist.more{mask-image:linear-gradient(to top,#000 calc(100% - 22px),transparent);
  -webkit-mask-image:linear-gradient(to top,#000 calc(100% - 22px),transparent)}
/* ONE LANE PER KEY, AND THE CLASS SAYS WHICH IS OUT. `.open` is the room's, `.bagopen` is
   yours, `.spellsopen` is what you can work. */
.plate .props.open .proplist:not(.ownlist){clip-path:inset(0 0 0 0);opacity:1;pointer-events:auto}
@media (prefers-reduced-motion:reduce){ .plate .props .proplist{transition:none} }

/* THE BAG, in the corner the lamp used to have, and its column beneath it.
   It opens DOWNWARD on purpose: the row of things is along the bottom, and a panel that covered it
   would make looking at the second thing mean closing the first — a rule this screen already lives
   by. `right` matches the lamp's old inset, so nothing moved sideways. */
/* THE SAME SIZE AND SHAPE AS A FACE ON THE OTHER EDGE. Two round targets on one picture, one per
   side, and they were 44px against clamp(44,12vw,54) — close enough to look like a mistake rather
   than a difference. They are the same thing now: a way into something, one tap wide.
   AND IT IS LEATHER, NOT RED. The backpack glyph is bright red in every system font, which on a
   dark blue picture reads as an alarm; `sepia` forces it to brown whatever the font's own colour
   was, which is the only way to say "leather" across three platforms without shipping a drawing. */
/* AND margin:0, BECAUSE THE NAME WAS ALREADY TAKEN. There is an unrelated `.bag` in this file —
   a plain list, `.bag li` — carrying `margin:8px 0 0`, and it applied here too: the bag sat
   exactly eight pixels below the faces it was asked to line up with, while its computed `top`
   read the same 40px as theirs. */
/* AND ITS FRAME IS THE EYE'S, not a face's. It was round and face-sized, on the reasoning that
   two round targets — one per edge — read as one kind of thing. Measured against what they
   actually hold, that was the wrong pairing: the faces are PEOPLE and the eye and the bag are
   both THINGS, one row the room owns and one row you own. The square with the soft corner is the
   shape this screen already uses for a thing, so the bag takes it, at the same 44px. */
.plate .bag{position:relative;margin:0;width:44px;height:44px;flex:0 0 auto;padding:0;
  border-radius:10px;border:1px solid rgba(232,165,74,.7);background:rgba(11,7,22,.72);
  color:var(--bright);font-size:19px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.45);
  display:flex;align-items:center;justify-content:center;overflow:visible}
/* THE FILTER IS ON THE GLYPH ALONE. Put on the button it takes the gold rim and the shadow with
   it, and the rim is what says this is the same kind of target as a face on the other edge. */
.plate .bag .glyph{line-height:1;filter:sepia(.9) saturate(.8) hue-rotate(-12deg) brightness(.9)}
.plate .bag[hidden]{display:none}
.plate .bag.on{background:#2a1a08;border-color:var(--accent)}
.plate .bag:active{transform:scale(.94)}
.plate .bag .badge{position:absolute;top:-4px;right:-2px;background:var(--go);color:#1a2a08;box-shadow:0 0 0 2px var(--bg);
  border-radius:999px;min-width:17px;height:17px;font:700 11px/17px ui-monospace,monospace;
  text-align:center;padding:0 3px}
.plate .bag .badge[hidden]{display:none}
/* AND THE COLUMN HANGS UNDER IT, clear of the round target above and of the row along the
   bottom — the two things it must never cover are the bag itself and the room's own row. */
/* THE BAG'S STRIP IS THE ROOM'S STRIP. It carries `.proplist` as well, so everything about how
   it folds, scrolls and hides is written once; this rule exists only to say it is the one that
   is showing. Two lists in one lane is what makes them exclusive without a line of script. */
.plate .props .ownlist[hidden]{display:none}
.plate .props.bagopen #bagStrip{clip-path:inset(0 0 0 0);opacity:1;pointer-events:auto}
.plate .props.spellopen #spellStrip{clip-path:inset(0 0 0 0);opacity:1;pointer-events:auto}
/* THE THIRD KEY. Same square, same lane, same fold — only the glyph differs, because what is
   behind it is a third owner of the same question and not a third kind of control. */
.plate .props .spellkey{position:relative;overflow:visible;flex:0 0 auto;
  border-color:rgba(232,165,74,.7);color:var(--bright);font-size:20px}
.plate .props .spellkey.on{background:#2a1a08;border-color:var(--accent)}
.plate .props .spellkey .badge{position:absolute;top:-4px;right:-2px;background:var(--go);
  color:#1a2a08;box-shadow:0 0 0 2px var(--bg);border-radius:999px;min-width:17px;height:17px;
  font:700 11px/17px ui-monospace,monospace;text-align:center;padding:0 3px}
.plate .props .spellkey .badge[hidden]{display:none}
.plate .bag.on{background:#2a1a08;border-color:var(--accent)}
.plate .ownlist button img{position:static;inset:auto;width:100%;height:100%;opacity:1;
  object-fit:cover;border-radius:9px}

/* SOMEBODY'S FACE INSTEAD OF THE ROOM. Same cross-fade as the room's own picture, one layer up,
   so when they stop speaking the room is simply still there underneath. */
.plate .plateface{z-index:2;opacity:0}
.plate .plateface.shown{opacity:1}

/* WHAT YOU CHOSE, while the die is being decided — low on the picture, on a band that darkens only
   behind the words. It used to dim the whole plate from the top down, and a family asked for the
   picture back: it is the thing being decided about (the beacon, the colossus at the gate). */
.plate .chose{position:absolute;inset:auto 0 0 0;z-index:7;display:flex;align-items:flex-end;
  background:linear-gradient(to top,rgba(11,7,22,.95) 0%,rgba(11,7,22,.8) 60%,rgba(11,7,22,0) 100%);
  padding:40px 14px 14px}
.plate .chose[hidden]{display:none}
/* AND WHILE IT IS BEING THOUGHT ABOUT, A PEN WRITES ABOVE IT. "Thinking…" was a word added to the
   family's own sentence, in the same colour, and it read as part of what they had pressed. A pen
   drawing a line of ink is the same news without a word, and it is the honest picture of what is
   happening: the next page is being written. Reduce motion: the pen and a short line, still. */
.plate .chose{flex-direction:column;align-items:stretch;justify-content:flex-end}
.plate .chose .quill{display:none}
.plate .chose.thinking .quill{display:flex;align-items:flex-end;height:22px;margin:0 0 6px 2px;color:var(--accent)}
.plate .chose .quill b{display:block;height:2px;margin-bottom:3px;border-radius:2px;background:currentColor;
  width:0;animation:quill-ink 1.8s ease-in-out infinite}
.plate .chose .quill svg{width:20px;height:20px;flex:none;margin-left:-3px;transform-origin:15% 85%;
  animation:quill-pen .45s ease-in-out infinite}
@keyframes quill-ink{0%{width:0;opacity:.95}70%{width:84px;opacity:.95}100%{width:84px;opacity:0}}
@keyframes quill-pen{0%,100%{transform:translateY(0) rotate(0)}30%{transform:translateY(-2px) rotate(-7deg)}
  65%{transform:translateY(1px) rotate(4deg)}}
@media (prefers-reduced-motion: reduce){
  .plate .chose .quill b{animation:none;width:44px}
  .plate .chose .quill svg{animation:none}
}
/* AND AS A CAPTION, while the result of it is being read. The scrim above is right for the roll
   screen, where the story is put away and the picture has nothing else to say; over a paragraph
   ABOUT that picture it would black out the thing being described. A band along the bottom, and
   the words that were pressed sitting on it. */
/* AND THE CLASS IS `caption`, NOT `band`. It was `band` — which is also the name of the panelled
   section on the hero sheet and in the journal, a rule that carries its own edge and its own
   corner radius. Nothing in `.plate .chose.band` declared a border, so `.band`'s one applied, and
   a family read a boxed rectangle drawn around the sentence that says what they pressed. This is
   the third time an unrelated list rule has reached into the plate by sharing a word (`.bag` sat
   a caption 8px low the same way), and the cure is the same: do not share the word. */
.plate .chose.caption{inset:auto 0 0 0;padding:26px 14px 10px;
  background:linear-gradient(to top,rgba(11,7,22,.95) 0%,rgba(11,7,22,.78) 58%,rgba(11,7,22,0) 100%)}
.plate .chose.caption span{max-height:4.4em;font-size:clamp(13px,3.4vw,15px)}
.plate .chose:not(.caption) span{max-height:8.8em}
/* NOT ITALIC. These are the words of the button that was just pressed — the table's own act, not
   a quotation and not an aside — and a whole paragraph of Georgia italic in Cyrillic is the
   hardest thing on this screen to read at 13px. */
.plate .chose span{display:block;width:100%;color:var(--bright);
  font-weight:400;font-size:clamp(14px,3.8vw,17px);line-height:1.45;
  max-height:100%;overflow-y:auto;scrollbar-width:none}
.plate .chose span::-webkit-scrollbar{display:none}

/* ---------- WHAT YOU JUST TAPPED ----------
 *
 * The strips say what is here; this says what one of them IS. It lives in the gap the two strips
 * leave — right of the faces, above the things, below the lamp — and that is not decoration: a
 * panel that covers the row it was opened from takes away the way back out of it, and a family
 * has to close it before they can look at the next thing.
 *
 * It is a sheet of glass over the picture like the hint and reach panels, because it is the same
 * kind of thing. What is different is that it does not take the whole plate.
 */
/* THE WHOLE READING AREA: the top of the room's picture down to the field. It used to live
   inside the picture, in the gap the two strips leave, so that the way back out stayed visible —
   and a person's state, their open subjects and five things to do do not fit in a gap. The close
   button is the way out; a panel too small to read is not one.
   AND IT IS ALWAYS THAT TALL NOW. It used to stop where its content stopped, on the reasoning
   that a thing with one act should not sit in a box two thirds empty — true of a panel that only
   listed acts, and no longer true of one that ends in what the thing IS. A box that changes
   height with every thing you tap is a box the eye has to find again each time, and the lesson
   is at the bottom of it. Fixed, so the last line is always in the same place. */
.look{position:absolute;inset:0 var(--pad) 0;z-index:12;
  display:flex;flex-direction:column;border-radius:12px;padding:10px 12px 10px;
  /* OPAQUE ENOUGH THAT NOTHING GHOSTS THROUGH IT. At .93 the room's name and the hint lamp
     showed faintly behind the panel's own heading, which reads as a rendering fault rather than
     as depth. */
  background:rgba(11,7,22,.985);border:1px solid var(--line);
  box-shadow:0 6px 24px rgba(0,0,0,.55)}
.look[hidden]{display:none}

/* AND THE ONE FOR A THING IS THE WHOLE SCREEN.
 *
 * It filled the reading area, which is everything above the decision band and the typing field —
 * and a family asked for the rest of it. The panel is no longer about tonight alone: it ends in
 * what the thing IS, and that is something somebody reads rather than glances at. Reading wants
 * the screen.
 *
 * So it is a child of the app column rather than of the picture, because a panel positioned
 * against the plate can never be taller than the plate, and `inset:0` here is the device. No
 * border and no corners: a card that fills the screen is not a card, and the edge would be a
 * line around nothing. The close button is the way out and it is where it has always been.
 */
/* AND THE NAME IS THE NAME OF A SCREEN, not a line squeezed in beside a button.
 *
 * On a card it sits left with the close button next to it, which is right for a card. On a
 * screen it read as something inserted — a family said so. Centred, bigger, and the × lifted
 * out of the row so the title is centred against the SCREEN rather than against whatever is
 * left over beside the button: a heading centred in the remainder is a heading nobody can tell
 * is centred. */
.look.full .look-head{display:block;position:relative;padding:2px 44px 8px;text-align:center}
.look.full .look-head b{display:block;color:var(--bright);
  font-weight:600;font-size:clamp(17px,5vw,21px);line-height:1.25;letter-spacing:.01em}
.look.full .look-head .hintclose{position:absolute;top:0;right:0}
/* And a line under it, the width of the words, so the name reads as a title rather than as the
   first sentence of the panel. */
.look.full .look-head b::after{content:'';display:block;width:46px;height:2px;margin:7px auto 0;
  border-radius:2px;background:var(--accent);opacity:.75}

/* HOW THINGS STAND HERE reads like a page, not like a card: left-aligned, and it scrolls inside the
   whole screen rather than stopping above the field nobody types into while reading it. */
.look.full.here .look-body{text-align:left;flex:1 1 auto;min-height:0;overflow-y:auto;color:var(--ink)}
.look.full.here .sealed{display:flex;align-items:center;gap:8px;color:var(--accent)}
.look.full.here .sealed svg{flex:0 0 16px}
.look.full{position:absolute;inset:0;max-height:none;border:0;border-radius:0;
  padding:10px var(--pad) calc(10px + env(safe-area-inset-bottom));
  /* AND FULLY OPAQUE. The card version sits at .985 because a sheet of glass over a picture is
     meant to read as depth; a panel that IS the screen has nothing to be in front of, and at
     .985 the decision band ghosted through the empty half of it — which reads as a rendering
     fault, which is the complaint that set .985 in the first place. */
  background:var(--bg)}

/* AND THE THING ITSELF, at the top, where a picture answers "which one is this" before any
   sentence does. It gives before anything else — a tall phone shows it whole and a short one
   shows a band of it — because it is the one part of this panel that is not words. */
.look-pic{flex:0 1 auto;min-height:0;margin-top:8px;border-radius:10px;overflow:hidden;
  border:1px solid var(--line);background:rgba(11,7,22,.6)}
.look-pic[hidden]{display:none}
.look-pic img{display:block;width:100%;height:100%;max-height:min(34vh,240px);
  object-fit:cover;object-position:center}
.look-head{display:flex;align-items:flex-start;gap:8px;flex:0 0 auto}
.look-head b{flex:1;min-width:0;color:var(--bright);
  font-weight:600;font-size:clamp(14px,3.9vw,16px);line-height:1.3}
/* WHAT IT IS, and one of the two things here that may need more room than they have.
   IT DOES NOT GROW INTO THE SLACK. The panel is full height now, so a body that grew would take
   every spare pixel and push the acts and the lesson to the floor — one sentence about a wheel
   in a box the height of the screen, with the buttons somewhere below it. It shrinks when it has
   to and otherwise stays its own size; the slack gathers at the bottom, where nothing is. */
.look-body{flex:0 1 auto;min-height:0;overflow-y:auto;margin-top:6px;
  color:var(--ink);font-weight:400;font-size:clamp(13px,3.5vw,15px);line-height:1.45;
  scrollbar-width:none;
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 18px),transparent);
  mask-image:linear-gradient(to bottom,#000 calc(100% - 18px),transparent)}
.look-body::-webkit-scrollbar{display:none}
.look-body p{margin:0 0 6px}
/* FOUR FACTS, FOUR LINES. `folkLines` joins them with newlines and a paragraph swallows them, so
   what they are doing, how they are with you, what has been asked and what is being held back ran
   together into one block a child has to unpick. */
.look-body .state{color:var(--muted);white-space:pre-line}
/* HOW THINGS STAND HERE: sections with a quiet heading, so four different kinds of answer read as
   four answers rather than as one run of text. The name of a measure or a person sits on its own
   line above what it says, which is what lets a family find "the gate" without reading all of it. */
.hereblock{margin:0 0 12px}
.hereblock > b{display:block;margin-bottom:4px;font:inherit;
  font-size:clamp(12px,3.1vw,13px);letter-spacing:.09em;text-transform:uppercase;color:var(--accent)}
.hereblock p{margin:0 0 5px}
.hereblock i{display:block;font-style:normal;color:var(--bright)}
/* AND WHAT CAN BE DONE WITH IT. The panel's whole purpose is to turn "what is that" into "and so
   I do this" without anybody having to find the words.
   A COLUMN, NOT A WRAPPING ROW. They were four short labels — "Look at it", "Use it" — and they
   sat beside each other happily. Now some of them are the author's own sentence about this thing
   ("I bring it down on purpose: Portcullis counterweight"), and a wrapping row of sentences is a
   crossword. One per line, left-aligned, and the column takes what is left of the panel: what a
   thing IS gives way to what can be DONE with it, because the second is why the panel is open. */
.look-acts{flex:0 1 auto;min-height:0;overflow-y:auto;scrollbar-width:none;
  display:flex;flex-direction:column;gap:6px;margin-top:8px}
.look-acts::-webkit-scrollbar{display:none}

/* WHAT THE THING IS, under everything there is to do with it.
 *
 * A row of acts is about tonight; this is not about tonight at all, and putting it above them
 * would make a child read a paragraph before reaching the buttons. It is the last thing on the
 * panel and the panel grows to hold it — which is why `.look` may now take the whole reading
 * area rather than only as much as its acts need. */
/* AND IT GIVES BEFORE THE PANEL DOES. Fixed height here would push the acts off the bottom on a
   short screen — the panel is capped at the reading area, so the last thing in it has to be able
   to shrink and scroll like the first. */
.look-teach{flex:0 1 auto;min-height:0;overflow-y:auto;scrollbar-width:none;
  margin-top:10px;padding-top:9px;border-top:1px solid var(--line)}
.look-teach::-webkit-scrollbar{display:none}
.look-teach[hidden]{display:none}
/* AND IT SAYS WHEN IT IS HOLDING MORE THAN IT SHOWS. On a short phone the lesson is the thing
 * that gives, and a frame that quietly keeps words out of sight has lost them — measured at
 * 640: 169px of box holding 235px of text, with a clean edge that reads as the end. Same answer
 * the story and the reveal already give: the words fade out at the bottom and a line underneath
 * says there is more. Both go when the end is reached. A scrollbar is not the answer — it lands
 * on top of the text and costs a word a line on a phone. */
.look-teach.more{-webkit-mask-image:linear-gradient(180deg,#000 86%,transparent);
  mask-image:linear-gradient(180deg,#000 86%,transparent)}
.look-more{flex:0 0 auto;display:none;padding-top:3px;text-align:center;
  color:var(--accent);font-weight:400;font-size:clamp(12.5px,3.3vw,13px);line-height:1.4}
.look-more.on{display:block}
.teach-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.teach-head b{color:var(--accent);font-weight:600;font-size:clamp(13px,3.5vw,15px);line-height:1.3}
/* AND THE TAG IS READ BEFORE THE PARAGRAPH, so it is small, tracked and the one coloured thing
   here: it changes what the sentence under it MEANS. */
.teach-tag{flex:0 0 auto;border-radius:999px;padding:1px 8px;
  font:700 clamp(12px,3vw,12.5px)/1.6 ui-monospace,monospace;letter-spacing:.06em;
  border:1px solid var(--line);color:var(--muted);background:rgba(11,7,22,.6)}
.teach-tag.real{color:#9fd6a0;border-color:rgba(159,214,160,.45)}
.teach-tag.legend{color:var(--bright);border-color:rgba(255,217,168,.45)}
.teach-tag.invented{color:#c3a7f0;border-color:rgba(195,167,240,.45)}
.look-teach p{margin:6px 0 0;color:var(--ink);
  font-weight:400;font-size:clamp(13px,3.5vw,14.5px);line-height:1.5}
.look-teach p[hidden]{display:none}
#lookTeachNotes[hidden]{display:none}
#lookTeachNotes .limit{color:var(--muted);font-size:clamp(12.5px,3.3vw,13.5px);line-height:1.4}
.teach-head[hidden]{display:none}
/* And what it IS keeps a floor of about two lines rather than a share of the box, so a thing with
   five things to do with it does not squeeze its own description to nothing. */
.look-body{flex:0 1 auto;max-height:52%}
/* WHAT IT DOES, AND IT IS THE MOST IMPORTANT LINE ON THE SCREEN.
 *
 * It was the same size and colour as everything else, under a picture that takes a third of the
 * panel, and it is the one sentence that answers why anybody tapped. Bigger, brighter, with room
 * around it — and the lines under it (a working's measure, what it will not do) stay small and
 * muted, because they are conditions on the sentence rather than the sentence. */
/* AND ON THE FULL PANEL IT IS NEVER CLIPPED. The card version caps it at 52% and fades the
 * bottom, which is right when it is one of two things in a small box — here it left «Неко се
 * свом тежином упире да га успори.» greyed out and unreachable, on the one line the whole panel
 * exists to deliver. Measured at 640: 33px tall holding 47px of sentence. It is one plain
 * sentence by construction, so it takes the room it needs and the things below it give. */
.look.full .look-body{margin-top:10px;text-align:center;
  flex:0 0 auto;max-height:none;overflow:visible;
  -webkit-mask-image:none;mask-image:none;
  font-weight:400;font-size:clamp(14.5px,4vw,16.5px);line-height:1.5;color:var(--bright)}
.look.full .look-body p{margin:0 0 7px}
.look.full .look-body p:last-child{margin-bottom:0}
.look.full .look-body .state,.look.full .look-body .limit{
  font-size:clamp(12.5px,3.3vw,13.5px);color:var(--muted);line-height:1.4}
/* AND WHAT THERE IS TO DO NEVER GIVES. A sentence held back can be scrolled to and says so; a
 * BUTTON held back is an act nobody knows is on offer, and the row is capped at five, so it can
 * be the thing that keeps its size. Measured at 640 before this: 16-24px of the last button
 * below the fold on all three panels, with a clean edge above it.
 * The picture gives first — it is the one part of this panel that is not words — down to a floor,
 * because a band thinner than that is not a picture of anything. Then the lesson, which says so. */
/* AND IT READS IN THE SAME VOICE AS THE STORY.
 *
 * This went the wrong way once. The buttons were monospace while the lead and the lesson were
 * Georgia, so the panel read as two screens stacked — and the fix was to make everything Georgia.
 * Measured afterwards: `.story-text` sets no family at all, so the NARRATIVE is the game's
 * monospace, and a panel in Georgia was the odd one.
 * AND THEN THE HEADINGS WENT TOO. They were kept on the grounds that every heading in the game
 * is serif, and a family looked at the result and said the old face was still there in places —
 * which it was, in eight: a panel's name, this lesson's heading, the band over the picture, the
 * list of doors, the hero's initial and the working rows at setup. The top of this stylesheet has
 * said ONE FACE FOR THE WHOLE GAME since the day the two were weighed against each other; these
 * were the places that never got the message. */
.look.full .look-body,.look.full .look-acts button,.look-teach p,#lookTeachNotes .limit,
.look-more{font-family:inherit}
.look.full .look-acts{flex:0 0 auto;overflow:visible}
.look.full .look-pic{flex:0 1 auto;min-height:92px}
.look-acts button{min-height:34px;padding:7px 11px;border-radius:9px;text-align:left;
  border:1px solid var(--line);background:var(--panel2);color:var(--ink);
  font:inherit;font-size:clamp(13px,3.4vw,14px);line-height:1.3;cursor:pointer}
.look-acts button:active{border-color:var(--accent);color:var(--bright)}

/* WHICH ONE IS OPEN. A ring in the accent, on the icon itself, so the panel and the thing it is
   about are visibly one thing rather than two that happen to be on screen together. */
.plate .cast button.on,.plate .props button.on{border-color:var(--bright);
  box-shadow:0 0 0 2px rgba(255,217,168,.35),0 2px 10px rgba(0,0,0,.5)}


/* ---------- THE WAYS ON ----------
 *
 * The choice row holds four things and reserves two of them for doors, which was a good rule
 * while a room had two ways out; the keep has ten rooms and up to four doors from one of them,
 * so WHICH two you were offered was an accident of what else was competing for the row. This is
 * the whole answer to "where can I go from here", one tap away and never in competition.
 *
 * It opens the way the map does — over the picture, inside the frame, with one × — because a
 * family has already learned that gesture on this screen and learning it twice is learning it
 * badly. */
/* AND IT SITS UNDER THE `i`, NOT IN THE FAR CORNER. The bottom-right was the only corner left
   when it arrived, and it put the one control that LEAVES the room diagonally opposite the three
   that open what is in it — a thumb crossing the whole picture to reach it, over the part of the
   picture the paragraph is usually about. The right-hand edge is where this screen keeps the
   things that are about the screen itself: the `i` re-reads the room, this leaves it. `top:88px`
   is the `i`'s own 40px from 40, plus the 8px gap the keys on the other edge use. */
.waysbtn{position:absolute;right:10px;top:88px;z-index:4;width:40px;height:40px;
  border-radius:999px;border:1px solid var(--line);background:rgba(11,7,22,.72);
  color:var(--bright);font-size:19px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(0,0,0,.45)}
.waysbtn[hidden]{display:none}
/* THE ARROW IS DRAWN, so it is the same size and weight on all three platforms. 20 of the 40,
   which is the proportion the glyphs on the other edge sit at inside their own keys. */
.waysbtn svg{width:20px;height:20px;display:block}
/* THE HIDDEN PLACES BEHIND THIS ROOM, as dots under the key (`paintHidden`), drawn like the keys' own:
   filled for found, hollow for still hidden; the gold ring is a place that has to be found. */
.waysbtn .dots{position:absolute;left:50%;bottom:-10px;transform:translateX(-50%);
  display:flex;gap:4px;pointer-events:none}
.waysbtn .dots i{width:7px;height:7px;border-radius:50%;border:1.5px solid var(--muted);background:var(--bg)}
.waysbtn .dots i.must{border-color:var(--accent)}
.waysbtn .dots i.on{background:var(--muted)}
.waysbtn .dots i.must.on{background:var(--accent)}
.waysbtn:active{background:#3a2a18;border-color:var(--accent)}
/* A NEW WAY OUT OF THIS ROOM HAS OPENED — a room unlocked, a hole that is a door now. The ring stays
   red until they leave the room, opened or not: it says "something changed here", and looking at
   it is not the same as using it. */
.waysbtn.fresh{border-color:var(--danger);box-shadow:0 0 0 2px var(--danger),0 0 12px rgba(224,85,69,.55)}

.waysmap{position:absolute;inset:0;z-index:6;border-radius:14px;background:rgba(11,7,22,.96);
  display:flex;flex-direction:column;align-items:center;justify-content:center;padding:14px}
.waysmap[hidden]{display:none}
.waystitle{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;
  letter-spacing:.12em;text-transform:uppercase;color:var(--accent);margin-bottom:10px}

/* THE ROOM YOU ARE IN IS THE MIDDLE OF IT, and everything it joins is around it. A grid rather
   than a drawn graph: the spokes are the whole point and a line between two boxes says the same
   thing as a box beside a box, without a canvas to maintain or coordinates to keep true. */
.waysnodes{display:flex;flex-wrap:wrap;gap:8px;align-items:stretch;justify-content:center;
  width:100%;max-width:340px;overflow-y:auto;scrollbar-width:none}
.waysnodes::-webkit-scrollbar{display:none}
.waysnodes button,.waysnodes .here{flex:1 1 46%;min-height:60px;border-radius:12px;padding:8px 10px;
  border:1px solid var(--line);background:var(--panel2);color:var(--ink);cursor:pointer;
  font-size:clamp(13px,3.4vw,15px);line-height:1.25;text-align:left;
  display:flex;flex-direction:column;justify-content:center;gap:3px}
.waysnodes button:active{border-color:var(--accent);transform:scale(.97)}
/* AND THEY HANG OFF IT. Without this it is a heading over a list, which is a different claim:
   a list says "here are some rooms" and a spoke says "this one joins THAT one". One stub of the
   line colour above each node and one below where you are standing, which is all a phone has
   room for and all the drawing has to say. */
.waysnodes .here{position:relative;margin-bottom:10px}
.waysnodes .here::after{content:'';position:absolute;left:50%;bottom:-10px;width:1px;height:10px;
  background:var(--accent)}
.waysnodes button{position:relative}
.waysnodes button::before{content:'';position:absolute;left:50%;top:-9px;width:1px;height:9px;
  background:var(--line)}
/* WHERE YOU ARE STANDING IS NOT A BUTTON, and it looks like the thing the others hang off:
   full width, the accent border, and no press. */
.waysnodes .here{flex:1 1 100%;cursor:default;border-color:var(--accent);background:#2a1a08;
  color:var(--bright);align-items:center;text-align:center}
.waysnodes .cost{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;
  letter-spacing:.06em;color:var(--muted)}
/* A DOOR THAT IS SHUT SAYS SO IN THE FICTION, never with a condition — the author writes that
   sentence and the screen prints it. It is not pressable and it is not hidden: a way you can see
   and cannot take yet is the map telling you there is more keep than this. */
.waysnodes button[disabled]{opacity:.55;cursor:default}
.waysnodes button[disabled]:active{transform:none}

/* ---------- THE LIBRARY, AT SETUP ----------
 *
 * Nine things to compare and three to take, on a phone, without scrolling — which is this
 * screen's rule and was broken by the first version: nine cards each carrying a description, a
 * measure and a limit put four on the screen and the other five below the fold, so a family chose
 * from the four they could see.
 *
 * TWO BANDS, AND ONLY ONE OF THEM READS. The grid is glanceable and fixed: three by three, a
 * glyph and a name, nothing to read. The card underneath is where the words are, and it is always
 * in the same place — the hero sheet's rule, and for its reason: a panel that appears and
 * disappears changes the height of the thing above it, and the thing above it is what somebody is
 * aiming at.
 */
/* THE GRID TAKES THE ROOM, because something has to. With the grid fixed and the card hugging
   its words, a tall phone was left with a hand's depth of nothing between the card and Continue —
   and nine tiles that could have been comfortable were small for no reason. The tiles grow into
   whatever is spare and stop at a floor, so a short phone gets a tappable grid and a tall one
   gets a generous one, and neither gets a void. */
/* AND IT STOPS GROWING. Left to take everything, a tall phone gave each tile 192px for a glyph
   and a name — a grid that looks stretched rather than generous. A floor and a ceiling, and
   whatever is spare after that is shared by the whole block rather than poured into the tiles. */
#stepSpells{justify-content:flex-start}
/* NINE ROWS, IN THE SHAPE THE BAG ALREADY USES: picture, name, and the one control on the right.
 *
 * It was a three-across grid of squares with the words underneath it, and two things were wrong
 * with that. A square holds a name only by clipping it — "Spark of Awakening" is three words in
 * a third of a phone — and the card that explains what you just tapped sat at the BOTTOM, which
 * on a short phone is under the fold. A row gives the name the whole width, and reading now
 * happens at the top of the page where a heading goes.
 *
 * THE LIST DOES NOT SCROLL AND MUST NOT NEED TO. Nine is the whole library, and a choice you have
 * to scroll to see is a choice between whatever happens to be on screen. The rows take what is
 * spare and stop at a floor of 38px, which is what makes nine of them fit a 640px phone. */
.spelllist{display:grid;grid-auto-rows:minmax(38px,52px);gap:4px;
  flex:0 1 auto;min-height:0;padding:2px 0}
.spellrow{display:flex;align-items:stretch;gap:6px;min-width:0}
/* READING IS THE LEFT OF THE ROW — the emblem AND the name, because the name is what somebody
   aims at when they want to know what a thing is, and leaving only the picture live would put
   the smallest target on the most-asked question. */
.spellrow .sp-read{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:9px;
  padding:4px 10px 4px 5px;border-radius:12px;border:1px solid var(--line);
  background:var(--panel2);color:var(--ink);cursor:pointer;text-align:left}
.spellrow .sp-read:active{transform:scale(.99)}
/* THREE STATES, AND THEY SAY DIFFERENT THINGS. Being READ is a soft outline — it means "this is
   what the card at the top is about". Being TAKEN is the accent. Somebody reading a working they
   have not taken must not think they have taken it. */
.spellrow.reading .sp-read{border-color:var(--muted);background:#1a1430}
.spellrow.on .sp-read{border-color:var(--accent);background:#2a1a08}
.st-icon{font-size:clamp(20px,6vw,24px);line-height:1}
/* THE NAME GETS THE WIDTH AND ONE LINE OF IT. A row is the whole screen wide, so the long names
   fit; the clamp to two lines is what stops a nine-row list from changing height when the
   language does. 13px is this file's floor for something a family reads. */
.st-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:clamp(13px,3.4vw,14px);line-height:1.2;letter-spacing:.01em;color:var(--ink);
  min-width:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.spellrow.on .st-name{color:var(--bright)}
/* AND THE BUTTON THAT TAKES IT, on the right, the same width on every row so the list has an
   edge. A tick as well as the colour, because colour alone is not an answer for somebody who
   does not see it, and three of nine taken has to be countable at a glance. */
/* ONE FIXED WIDTH, NOT A MINIMUM. Written as `min-width` the button grew with its own label, so
   "PUT IT BACK" made a wider button than "TAKE IT" — nine rows with a ragged right edge, and the
   name beside a taken one losing width for saying so. A basis the label has to live inside, and
   it may take two lines to do it. */
.spellrow .sp-pick{flex:0 0 clamp(92px,26vw,112px);padding:0 8px;border-radius:12px;
  border:1px solid var(--accent);background:transparent;color:var(--bright);cursor:pointer;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:clamp(11px,2.9vw,12px);
  letter-spacing:.03em;text-transform:uppercase;line-height:1.15;
  display:flex;align-items:center;justify-content:center;text-align:center}
.spellrow .sp-pick:active{transform:scale(.97)}
.spellrow .sp-pick.on{background:#2a1a08}
/* A NON-BREAKING SPACE, because a plain one collapses inside a centred flex line and the tick
   ends up welded to the word: «✓ВРАЋАМ». */
.spellrow .sp-pick.on::before{content:'✓\00a0'}
.spellrow .sp-pick:disabled{opacity:.35;cursor:default;border-color:var(--line);color:var(--muted)}

/* THE CARD THAT NEVER MOVES. It takes what is left of the screen and scrolls inside itself if a
   description is long, so the grid above it never shifts under a finger. */
/* IT HUGS WHAT IS IN IT. Written as `flex:1 1 auto` the card took everything the grid left,
   which on a tall phone is a hand's depth of empty panel with a button stranded at the bottom of
   it. `0 1 auto` gives it the height of its own words and no more, and `min-height:0` with the
   scroll keeps a long description inside the screen rather than pushing the grid off it. */
.spellcard-read{flex:0 0 auto;display:flex;flex-direction:column;gap:4px;
  margin:0 0 8px;padding:9px 12px;border-radius:12px;max-height:38vh;
  border:1px solid var(--line);background:var(--panel2);overflow-y:auto;scrollbar-width:none}
.spellcard-read::-webkit-scrollbar{display:none}
.sr-head{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.sr-icon{font-size:22px;line-height:1}
.sr-head b{font-size:clamp(16px,4.4vw,18px);color:var(--bright)}
.sr-desc{font-size:clamp(13px,3.5vw,15px);line-height:1.4;color:var(--ink)}
/* THE MEASURE AND THE LIMIT ARE SPECIFICATION, not prose, so they are monospace and quiet — and
   the limit is in the accent because it is the half that decides: "it turns ONE thing at a time"
   is the whole difference between a ward and invulnerability. */
.sr-meta,.sr-limit{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;
  line-height:1.35;letter-spacing:.02em}
.sr-meta{color:var(--muted)}
.sr-limit{color:var(--accent)}
/* THE CARD HAS NO BUTTON ANY MORE. Taking used to happen here, which meant the one control on
   the screen was somewhere other than the thing it was about; it is on the row now, beside the
   name, where somebody's thumb already is. */
.spellnote{flex:0 0 auto;padding-top:8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:13px;color:var(--muted);text-align:center}


/* ---------- THE HERO SHEET, IN ONE COLUMN ----------
 *
 * It was a portrait beside a column of gear squares, which cost the picture a third of its width
 * and gave the gear a column too narrow for names like "Недовршена рукавица од олујног стакла".
 * The portrait is what this panel opens with — it is the person — so it takes the whole width,
 * and everything that describes him reads down the page underneath it in the order the questions
 * are asked: what am I good at, what am I wearing, and then whatever was just tapped.
 *
 * THE BAG IS NOT HERE. It lives in the corner of the picture now, one tap from the moment it is
 * decided about, and a second copy on this page would be two places to keep one fact true.
 */
/* TOP-ALIGNED, NOT SPREAD. `space-between` was written for a sheet with six rows in it; with
   four it pushed the first into the ceiling, the last onto the button and left two lakes in the
   middle — which is the note this file already carries about `.airy`, one panel over. */
.herosheet{justify-content:flex-start}
/* THE PORTRAIT TAKES THE WIDTH AND WHATEVER HEIGHT IS SPARE. It has no intrinsic box — the image
   inside it is absolutely positioned — so without a ratio it collapsed to nothing and the panel
   opened on an empty frame. 0.63 is the shape the generator actually draws (1024×1536), so the
   frame and the picture agree and at most a pixel is cut. */
.herosheet .hero-figure.wide{width:100%;flex:0 1 auto;min-height:0;aspect-ratio:.63}
.herosheet .wornbox{flex:0 0 auto;padding-top:4px}
.herosheet .wornbox .label{margin:0 0 5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
/* THREE SQUARES IN A ROW, and they keep the shape a slot has everywhere else in this game:
   the picture fills it and the name sits over a gradient along the bottom. The only change is
   that these names WRAP — the shared rule ellipsises on one line, which was written for a slot
   in a grid of ten and turns "Недовршена рукавица од олујног стакла" into "Недовршена…". */
.herosheet .slots.worn{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}
.herosheet .slots.worn .slot-name{white-space:normal;overflow:visible;font-size:12px;
  line-height:1.2;padding:16px 4px 4px}


/* ---------- A WORKING'S EMBLEM ----------
 *
 * Drawn once for the life of the adventure and shown in three places: the library at setup, the
 * square in the corner of the picture, and the panel that square opens. The author's glyph stands
 * in while it is drawn, so none of the three is ever an empty box.
 */
.spellrow .st-icon{display:grid;place-items:center;flex:0 0 auto;
  width:clamp(30px,9vw,38px);height:clamp(30px,9vw,38px);
  font-size:clamp(20px,6vw,24px);line-height:1}
/* THE EMBLEM FLOATS, IT IS NOT A CHIP. Each is drawn on a plain near-black ground — which is
   what makes it read as one clear silhouette — and dropped into a panel-coloured card that ground
   becomes a black square with a picture in it, nine of them, which is worse than the glyphs were.
   `screen` drops the near-black to nothing and leaves the motif, so what is in the card is the
   emblem itself. It works because these are bright motifs on a dark ground by construction: the
   prompt asks for exactly that, and the one rule that keeps it true is in `authoredSpellPrompt`. */
/* AND THE EDGE OF THE GROUND GOES WITH IT. The drawn ground is a very dark navy rather than pure
   black, so `screen` lifts it a little and leaves a faint square — visible on the amber of a
   taken tile, which is exactly where somebody is looking. A radial fade takes the corners to
   nothing, so what is left is the motif and no box around it. */
.spellrow .st-icon img{width:100%;height:100%;object-fit:contain;mix-blend-mode:screen;
  mask-image:radial-gradient(closest-side,#000 62%,transparent 100%);
  -webkit-mask-image:radial-gradient(closest-side,#000 62%,transparent 100%)}
/* AND THE SELECTOR IS THE CLASS ITSELF, because `.lookpanel` does not exist.
   The panel is `#lookPanel` with class `look` — so both rules written for the emblem beside the
   name matched NOTHING, and an `<img>` with no width is its natural size: measured at 1024×1024
   inside a 473px panel, head 1049px tall, the description and the one offer pushed to top 1130.
   A family tapped a working and got a blank sheet with a corner of the emblem on it. The class
   name is already the scope; an ancestor in the selector was one more thing to get wrong. */
.sr-icon img,.lookicon{mix-blend-mode:screen;
  mask-image:radial-gradient(closest-side,#000 62%,transparent 100%);
  -webkit-mask-image:radial-gradient(closest-side,#000 62%,transparent 100%)}
.sr-icon{display:inline-grid;place-items:center;width:26px;height:26px;font-size:22px;line-height:1}
.sr-icon img{width:100%;height:100%;object-fit:contain}
/* In the strip the square is 44px and the emblem fills it, like every other thing in that lane. */
.plate .props #spellStrip button img{position:static;inset:auto;width:100%;height:100%;opacity:1;
  object-fit:cover;border-radius:9px}
/* And beside the name in the panel it is small, because there the words are the point. */
.lookicon{width:22px;height:22px;object-fit:contain;vertical-align:-4px;margin-right:6px}
/* AND NOTHING IN A PANEL HEAD MAY GROW PAST IT. The line above is the fix; this is the floor
   under it, so the next picture put beside a name cannot take the panel away with it. */
.look-head img{max-width:26px;max-height:26px}

/* WHY THE DOORS ARE SHUT, said once above them. */
/* THE SIGN OF A HIDDEN PLACE, in the thing's panel and under the doors: a door, and what can be
   seen — never where it goes. */
.look-body p.secret,#waysNodes .ways-secret{display:flex;gap:10px;align-items:flex-start;text-align:left;
  margin:8px 0;padding:8px 10px;border-left:3px solid var(--muted);background:rgba(255,217,168,.06);
  color:var(--bright);font-size:inherit;line-height:1.4}
.look-body p.secret,#waysNodes .ways-secret.must{border-left-color:var(--accent)}
.look-body p.secret svg,#waysNodes .ways-secret svg{flex:0 0 22px;width:22px;height:22px;margin-top:1px}
.look-body p.secret b,#waysNodes .ways-secret b{color:var(--accent);font-weight:600}
#waysNodes .ways-secret i{display:block;margin-top:3px;font-style:normal;color:var(--ink)}
#waysNodes button.far::before{display:none}
#waysNodes .ways-far{flex:1 1 100%;margin:10px 0 2px;text-align:center;color:var(--accent);font:600 clamp(12px,3vw,13px)/1.2 Georgia,serif;letter-spacing:.12em;text-transform:uppercase}
#waysNodes .ways-held{margin:0 0 6px;text-align:center;color:var(--bright);font-size:clamp(13px,3.4vw,15px);line-height:1.35}

/* WHO AND WHAT IS IN THIS ROOM, on the first visit, after the room has described itself.
   The whole screen and no close button: any tap closes it, because it is something to look at
   and then put away, not something to act on. People first, as faces; things under them, square,
   because a thing is not a person (the same rule the two strips on the picture keep). */
/* ALWAYS ONE SCREEN, however many there are. `--ri` is the size of a picture and the screen sets
   it (`fitRoomIntro`): it starts large and steps down until everything fits. The names never go
   under 13px — the pictures give way, not the words — so a figure is never narrower than its
   caption needs. Nothing scrolls. */
.roomintro{--ri:104px;--rig:16px;justify-content:center;gap:calc(var(--rig) * 1.2);
  padding:clamp(12px,3vh,32px) clamp(12px,4vw,24px);cursor:pointer;overflow:hidden}
.roomintro .ri-folk,.roomintro .ri-things{display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--rig) calc(var(--rig) * .8)}
.roomintro .ri-sec{display:flex;flex-direction:column;gap:calc(var(--rig) * .7)}
.roomintro .ri-sec[hidden]{display:none}
.roomintro .ri-thingsec.apart{border-top:1px solid var(--line);padding-top:calc(var(--rig) * 1.1)}
.roomintro .ri-head{text-align:center;color:var(--accent);font-size:clamp(12px,3.4vw,14px);
  letter-spacing:.16em;text-transform:uppercase;font-weight:600}
.roomintro figure{margin:0;display:flex;flex-direction:column;align-items:center;gap:5px;
  width:max(var(--ri),78px)}
.roomintro .ri-pic{width:var(--ri) !important}
.roomintro .ri-pic{aspect-ratio:1;display:grid;place-items:center;overflow:hidden;
  background:var(--panel2);border:1px solid var(--line);color:var(--muted);
  font:600 clamp(20px,6vw,28px) Georgia,serif}
.roomintro .ri-pic img{width:100%;height:100%;object-fit:cover;display:block}
.roomintro .ri-folk .ri-pic{border-radius:999px;border:2px solid var(--accent)}
.roomintro .ri-folk .rival .ri-pic{border-color:var(--danger)}
.roomintro .ri-things .ri-pic{border-radius:10px}
.roomintro figcaption{color:var(--ink);text-align:center;line-height:1.2;
  font-size:clamp(13px,3.6vw,15px);overflow-wrap:break-word;hyphens:auto}
.roomintro .ri-folk figcaption{color:var(--bright);font-weight:600}
.roomintro.tight figure{width:max(var(--ri),62px)}
.roomintro.tight figcaption{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}

/* MEANWHILE… — news from elsewhere in the keep. The room darkens (it is not about this room) and
   the mark of the news stands in the middle of it; the page itself reads as a dispatch: the
   heading in the accent, the sentence larger and in the serif the headings use. */
.plate .dispatch-mark{position:absolute;inset:0;z-index:4;display:grid;place-items:center;
  opacity:0;transform:scale(.85);pointer-events:none;
  transition:opacity .5s ease,transform .6s cubic-bezier(.2,.8,.2,1);
  filter:drop-shadow(0 0 22px rgba(232,165,74,.45))}
.plate .dispatch-mark svg{width:clamp(96px,30vw,140px);height:auto}
.plate .dispatch-mark .ring{fill:var(--accent)}
.plate .dispatch-mark .core{fill:var(--panel2);stroke:var(--bright);stroke-width:2.5}
.plate .dispatch-mark .sign{fill:none;stroke:var(--bright);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.plate .dispatch-mark .sign .fill{fill:var(--accent);stroke:var(--bright);stroke-width:2}
.plate .dispatch-mark.on{opacity:1;transform:scale(1)}
.plate .dispatch-mark[hidden]{display:none}
.plate.dispatching .plateimgs{filter:brightness(.32) saturate(.6);transition:filter .6s ease}
.plate .plateimgs{transition:filter .6s ease}
.story.dispatch .who{color:var(--accent);letter-spacing:.14em}
/* The game's own typeface — a second one on one page read as a different screen, not as news. */
.story.dispatch .body{font-size:clamp(16px,4.4vw,19px);line-height:1.5;color:var(--bright);white-space:pre-line}
@media (prefers-reduced-motion:reduce){
  .plate .dispatch-mark{transition:none;transform:none}
  .plate.dispatching .plateimgs,.plate .plateimgs{transition:none}
}

/* What they pressed belongs to the turn, not to the news from elsewhere: it steps back while a
   "meanwhile" page is read, and comes back with the next page that is about the room. */
.plate.dispatching .chose{opacity:0;transition:opacity .3s ease}

/* MEANWHILE, AS A PICTURE. The news has its own drawing now: on the desk it is the left page, whole
   and undarkened — and the room's name does not sit over it, because it is not this room. On a
   phone the news takes the whole column: the picture at the top of the page, the heading, the
   sentence; the plate steps aside. Until the picture lands the page carries the news's mark. */
.plate.dispatchpic .placename,.plate.dispatchpic .chose{opacity:0;transition:opacity .3s ease}
.story .dispatch-art{display:none}
.story.dispatch .dispatch-art{display:grid;place-items:center;position:relative;width:100%;
  aspect-ratio:4/3;max-height:52vh;margin:0 0 14px;border-radius:var(--radius);overflow:hidden;
  background:var(--panel2);border:1px solid var(--line)}
.story.dispatch .dispatch-art img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .5s ease}
.story.dispatch .dispatch-art.has-art img{opacity:1}
.story.dispatch .dispatch-art.has-art .mark{display:none}
.story.dispatch .dispatch-art .mark svg{width:clamp(80px,26vw,120px);height:auto;
  filter:drop-shadow(0 0 18px rgba(232,165,74,.4))}
.story .dispatch-art .ring{fill:var(--accent)}
.story .dispatch-art .core{fill:var(--panel2);stroke:var(--bright);stroke-width:2.5}
.story .dispatch-art .sign{fill:none;stroke:var(--bright);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.story .dispatch-art .sign .fill{fill:var(--accent);stroke:var(--bright);stroke-width:2}
.story.dispatch .who{display:block}
@media not all and (min-width:1024px) and (min-aspect-ratio:5/4){
  .stretch:has(#story.dispatch:not([hidden])) #scenePlate{display:none}
}
/* Only while the news is ON SCREEN: the story put away after a page of news kept its class, and the
   plate — the picture, the people, every key on it — stayed gone for the rest of the room. */
body.device .stretch:has(#story.dispatch:not([hidden])) #scenePlate{display:none}
@media (prefers-reduced-motion:reduce){ .story.dispatch .dispatch-art img{transition:none} }

/* ======================================================================================
   THE OPEN BOOK — the desktop layout.
   On a wide, landscape screen the game is a book lying open: the PICTURE is the left page, the
   whole height of the screen, and the WORDS are the right page — the story, the question, the
   row and the field. The panels (a thing, a person, how things stand here, who is in the room)
   open over the right page, so the picture stays in view while you read about what is on it.

   Nothing here touches the phone: it is one block behind a media query that a phone, a tablet
   held upright and the phone frame (ctrl+M, `body.device`) never match. The rules are prefixed
   with `html body:not(.device) #app.book` — only the location screen carries `.book`; the setup and
   the old world map also have an `#app` — so they win by SPECIFICITY rather than by their place in the
   file — this file has twice been caught out by a block that lost on order — and in particular
   beat `.stretch:has(#story[hidden]) #scenePlate`, which is (2,2,0).

   `.stretch` dissolves (`display:contents`) so the picture and the story become cells of the
   app's own grid; the panels are absolutely positioned grid children, and an absolutely
   positioned child placed on the grid takes that grid AREA as its box — which is how a panel
   covers exactly the right page and nothing else.
   ====================================================================================== */
@media (min-width:1024px) and (min-aspect-ratio:5/4){
  html body:not(.device) #app.book{
    max-width:1560px;width:100%;
    display:grid;grid-template-columns:minmax(0,1.12fr) minmax(400px,1fr);
    grid-template-rows:auto minmax(0,1fr) auto;
    --book-pad:clamp(20px,2.2vw,36px);--book-foot:18px;
    column-gap:var(--book-pad);padding:0 var(--book-pad) var(--book-foot);
    --pad:0px}
  html body:not(.device) #app.book > .top{grid-column:1 / -1;padding:12px 0 14px}
  /* THE ADVENTURE BEHIND THE BOOK, as the homepage sketch has its hero: the cover at a third of its
     strength, a lamplight glow, and the night closing in at the edges so the panels stay the thing
     you read. Fixed, so it does not scroll with anything. Set from the cover once it is known
     (`--backdrop`); until then, and without one, it is simply the night. */
  html body:not(.device):has(#app.book)::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
    background:
      radial-gradient(60% 50% at 70% 30%, rgba(232,165,74,.16), transparent 70%),
      radial-gradient(120% 90% at 50% 50%, transparent 40%, var(--bg) 100%),
      var(--backdrop, none) center 30% / cover no-repeat;
    opacity:.38;filter:saturate(.9)}
  html body:not(.device):has(#app.book){background:var(--bg);isolation:isolate}
  /* A GHOST BUTTON IS GHOSTLY AGAINST THE NIGHT, NOT AGAINST A PICTURE. "Suggest actions" is meant
     to be the quiet one under the field — transparent over the plain background a phone has — and
     with the adventure behind the book the castle showed through it. It keeps its quiet look and
     gets the panels' own ground. */
  html body:not(.device) #app.book .act.ghost{background:var(--panel);border-color:var(--line)}
  html body:not(.device) #app.book > .stretch{display:contents}
  /* THE LEFT PAGE: the picture, top to bottom. */
  html body:not(.device) #app.book #scenePlate.plate{grid-column:1;grid-row:2 / span 2;
    height:auto;min-height:0;align-self:stretch}
  /* THE RIGHT PAGE: the words on top, the row under them. */
  html body:not(.device) #app.book #story{grid-column:2;grid-row:2;min-height:0}
  html body:not(.device) #app.book > .actions{grid-column:2;grid-row:3;padding:12px 0 0}
  /* While the action is being chosen the story is put away, and the row has the whole page —
     set on the page's middle rather than dropped to its foot, where a long row would leave a
     page of nothing above it. */
  html body:not(.device) #app.book:has(#story[hidden]) > .actions{grid-row:2 / span 2;align-self:center;
    max-height:100%;padding:0}
  /* …and the caption sits on the field it is about. The page band (count and the way out of the
     story) is empty while choosing, but as a flex child it still bought a second gap between the
     caption and the row — the two read as two blocks a hand apart. */
  html body:not(.device) #app.book:has(#story[hidden]) #pagerow{display:none}
  /* AND THE PANELS ARE THE RIGHT PAGE, not the screen. */
  /* An absolutely placed child on the LAST column and row is bounded by the container's padding
     edge, not its content edge — measured, the panel ran 32px past the page to the window's
     right edge and 18px under its foot. The page's own margins are given back explicitly. */
  html body:not(.device) #app.book > .look.full{grid-column:2;grid-row:2 / span 2;
    inset:0 var(--book-pad) var(--book-foot) 0;border-radius:var(--radius);
    /* A PAGE HAS MARGINS. `padding:4px 0 0` was written while the panel had no ground of its
       own; once it was a dark page over the right half, every line ran into its left edge —
       the description, the button, the lesson — and a family read the panel as broken. */
    padding:12px 24px 18px;
    /* Above the band under the story (whose buttons showed through the page's foot), below the
       full-screen sheet (30). */
    z-index:25}
  html body:not(.device) #app.book > .look.full.roomintro{padding:10px 0}
  /* AND THE THING IS SHOWN WHOLE. A square drawing in a page-wide box, cropped to a band, showed
     the middle of a table's legs; on a desk there is room for the whole of it. */
  html body:not(.device) #app.book .look-pic img{max-height:min(38vh,320px);object-fit:contain}

  /* TYPE FOR A PAGE READ AT ARM'S LENGTH. A phone's 17px is small on a desk, and a line as wide
     as half a monitor is no longer a line anybody reads: the words grow, and the page keeps a
     reading measure. */
  html body:not(.device) #app.book .story{padding:22px 30px 30px}
  html body:not(.device) #app.book .story-text{font-size:clamp(18px,1.25vw,21px);line-height:1.6}
  /* The news's picture is the left page here; the page itself is the words. */
  html body:not(.device) #app.book #story.dispatch .dispatch-art{display:none}
  /* AND THE SMALL LINES AROUND IT GROW WITH IT. Everything inside the story box already follows the
     page's size; what sits outside it — the words of what was pressed, on the picture, and the
     line saying there is more below — was still the phone's 14–15px on a page of 21. */
  html body:not(.device) #app.book .plate .chose span,
  html body:not(.device) #app.book .plate .chose.caption span{font-size:clamp(17px,1.15vw,19px);max-height:6em}
  html body:not(.device) #app.book .story .scroll-cue,
  html body:not(.device) #app.book .morehint{font-size:16px}
  html body:not(.device) #app.book .act{font-size:17px;min-height:52px}
  html body:not(.device) #app.book .act.primary{font-size:18px}
  html body:not(.device) #app.book .prompt span{font-size:17px;line-height:1.4}
  html body:not(.device) #app.book .ask textarea{font-size:17px}
  html body:not(.device) #app.book .look-body,
  html body:not(.device) #app.book .look-teach p{font-size:17px;line-height:1.55}

  /* A HAND ON A MOUSE CAN POINT. The phone has no hover, so nothing here changes what a finger
     sees; on a desk the row answers the pointer the way a page answers the eye. */
  html body:not(.device) #app.book .act:hover{border-color:var(--accent);background:var(--panel2)}
  html body:not(.device) #app.book .act.primary:hover{filter:brightness(1.12)}
  html body:not(.device) #app.book .plate button:hover{filter:brightness(1.15)}
  /* THE THINGS ON THE PICTURE STAY THE SIZE OF THINGS. On a phone the strip is four columns of
     the picture's width; on a page twice as wide that made squares of 155px in two rows over half
     the picture. On the desk they keep a size a pointer finds easily, and more fit in a row. */
  html body:not(.device) #app.book .plate .props .proplist{grid-template-columns:repeat(auto-fill,88px);
    justify-content:start}
  /* THE NUMBER A KEY PRESSES, on the button it presses — small and quiet, at the left edge, and
     only on a desk: a phone has no keys and the numbers would be noise there. */
  html body:not(.device) #app.book #choices{counter-reset:act}
  html body:not(.device) #app.book #choices > button{position:relative;counter-increment:act}
  html body:not(.device) #app.book #choices > button:nth-child(-n+9)::before{content:counter(act);
    position:absolute;left:12px;top:50%;transform:translateY(-50%);
    font:12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted);
    border:1px solid var(--line);border-radius:6px;padding:3px 6px;opacity:.85}
  html body:not(.device) #app.book #choices > button:not(.primary){padding-left:44px}
  /* The phone-frame hint is a tool for the person building this, and in the book it sits on the
     corner of the picture: it says what it has to say, then goes. */
  #deviceHint{animation:hint-away 1s ease 6s forwards}
}
@keyframes hint-away{to{opacity:0}}

/* While a chapter's title page holds the plate, the room's name steps aside (`openingPic`). */
.plate.chapterpic .placename{opacity:0;transition:opacity .3s}

/* ---- TWO FACES, EACH WITH ONE JOB ----
   The words a family READS stay in the game's own face; what NAMES something — a place, who is
   speaking, what kind of moment this is, a panel's title, a section's label — is set in Georgia,
   the face the moments on the picture already use. A heading in the same face as the line under it
   read as one list; a name in a book face reads as a name. Spaced capitals where the label is
   capitals, so Georgia's own capitals breathe. Written last and wide, so it wins by specificity and
   by place over every older rule that set a face on these. */
:root{--title-font:Georgia,'Times New Roman',serif}
html body :is(.plate .placename, .story-text .who, .story-text .verdict-how, .story-text .note-who,
  .prompt b, .look.full .look-head b, .look-head b, .teach-head b, .waystitle, .pagehead, .band h3,
  .adventurelog h3, .hinthead b, .hint b, .sheetover h2, .roomintro .ri-head, .bigcard b, .youtext b,
  .tilename, .figbadge .figrole){
  font-family:var(--title-font)}
html body :is(.plate .placename, .story-text .who, .story-text .verdict-how, .prompt b, .waystitle,
  .roomintro .ri-head, .sheetover h2){letter-spacing:.12em}

/* The job's parts in the scroll's panel: what is left in the reading colour, what is done in green. */
.hereblock p.jobpart.done{color:var(--go)}
.hereblock p.jobpart.now{color:var(--accent);font-weight:600}
/* A conclusion is a sentence, not a name: set as one, not in shouting capitals. */
.plate .moment.concluded .m-label{text-transform:none;letter-spacing:.01em;font-size:clamp(17px,4.6vw,21px);line-height:1.35;max-width:30em}
/* THE LAMP BREATHES when the table has wandered a room without a new tick (`IDLE_NUDGE`). */
#hintBtn.nudge{animation:lamp-nudge 1.6s ease-in-out infinite;box-shadow:0 0 0 2px var(--accent)}
@keyframes lamp-nudge{50%{box-shadow:0 0 14px 4px rgba(232,165,74,.75)}}
@media (prefers-reduced-motion:reduce){#hintBtn.nudge{animation:none}}

/* "SOMETHING IS WRONG HERE", at the foot of the scroll panel: a note and one button. */
.look.here .hereblock.report textarea{ width:100%; box-sizing:border-box; margin:6px 0; font:inherit;
  font-size:clamp(14px,3.8vw,16px); color:var(--ink); background:var(--panel2); border:1px solid var(--line);
  border-radius:8px; padding:8px; resize:vertical }
.look.here .hereblock.report .act{ width:100% }
.look.here .hereblock.report .reportdone{ color:var(--accent); margin-top:6px }
.rollcard .rc-tip{ font-size:clamp(13px,3.6vw,15px); color:var(--muted); margin-top:6px; line-height:1.35 }

/* A HEADING IS NOT SMALLER THAN THE LINE UNDER IT. Georgia at the same pixel size reads smaller than
   the monospace body beside it (its lower-case is shorter), so the adventure card's title sat under
   its own pitch, and «ПРИПОВЕДАЧ» under its paragraph. Both are now a step above what they head. */
html body .bigcard b{font-size:clamp(17px,4.6vw,19px);line-height:1.25}
html body .story-text .who{font-size:1.08em}

/* THE TWO HALVES ARE PRESSABLE: each answers its own gold button (see `offer`). The box itself stays
   out of the way; only the clipped halves take the pointer, and clip-path clips the hit test too. */
html body .plate .split .half{pointer-events:auto;cursor:pointer}
html body .plate .split .num{pointer-events:none}
html body .choices .act.hot, html body #choices .act.hot{border-color:var(--bright);box-shadow:0 0 0 2px var(--accent)}
html body .plate .split .seam{pointer-events:none}

/* THE ROOM AT A GLANCE (`#roomBoard`): the whole column, the room's tiles sharing the height between
   them — done ticked and grey, still going on in gold, a fight in red with its hearts, what it hides
   dark — and the ways on apart, under a line. One screen however many there are: past five, two
   columns. Higher than `.look.full` alone, so it wins over it by specificity, not by place. */
.look.full.roomboard{display:flex;flex-direction:column;justify-content:flex-start;gap:10px;
  padding:14px 14px 12px;overflow:hidden}
.look.full.roomboard[hidden]{display:none}
.roomboard .rb-sec{display:flex;flex-direction:column;gap:6px;min-height:0}
.roomboard .rb-sec:first-child{flex:1 1 auto}
.roomboard .rb-sec[hidden]{display:none}
.roomboard .rb-tiles{display:flex;flex-direction:column;gap:6px;flex:1 1 auto;min-height:0}
.roomboard .rb-tiles.two{display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:minmax(44px,1fr)}
.roomboard .rb-doors{flex:0 1 auto}
.roomboard .rb-on{border-top:1px solid var(--line);padding-top:10px;flex:0 1 auto}
.roomboard .rb-tile{flex:1 1 0;max-height:118px;min-height:46px;display:flex;align-items:center;gap:10px;
  padding:5px 8px 5px 5px;border-radius:12px;border:1px solid var(--line);background:var(--panel2);
  color:var(--ink);text-align:left;cursor:pointer;font:inherit;min-width:0;overflow:hidden}
.roomboard .rb-doors .rb-tile{max-height:64px}
.roomboard .rb-pic{height:100%;max-height:104px;min-height:36px;aspect-ratio:1;flex:0 0 auto;border-radius:9px;
  overflow:hidden;display:grid;place-items:center;background:var(--panel);color:var(--muted);
  font:18px Georgia,serif}
.roomboard .rb-pic img{width:100%;height:100%;object-fit:cover;display:block}
.roomboard .rb-pic svg,.roomboard .rb-mark svg{width:60%;height:60%}
.roomboard .rb-text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.roomboard .rb-text b{font-family:Georgia,serif;font-weight:normal;font-size:clamp(14px,3.9vw,16px);
  line-height:1.25;color:var(--bright);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.roomboard .rb-text small{font-size:13px;line-height:1.3;color:var(--muted);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.roomboard .rb-mark{flex:0 0 auto;width:26px;height:26px;display:grid;place-items:center;color:var(--accent);font-size:18px}
.roomboard .kind-open{border-color:var(--accent)}
.roomboard .kind-done{opacity:.72;cursor:default}
.roomboard .kind-done .rb-pic img{filter:grayscale(1)}
.roomboard .kind-done .rb-mark{color:#6fcf7a;font-size:20px}
.roomboard .kind-fight{border-color:var(--danger)}
.roomboard .kind-fight .rb-pic{border:2px solid var(--danger);border-radius:999px}
.roomboard .kind-fight .rb-text small{color:var(--danger);letter-spacing:.1em}
.roomboard .kind-fight .rb-mark{color:var(--danger)}
.roomboard .kind-secret{background:#06040d;border-style:dashed}
.roomboard .kind-secret.must{border-color:var(--accent)}
.roomboard .kind-secret .rb-pic{font-size:24px;color:var(--accent)}
.roomboard .kind-secret .rb-pic img{filter:brightness(.4) saturate(.6)}
.roomboard .kind-keys .rb-pic{background:transparent}
.roomboard .kind-keys.finished{opacity:.72}
.roomboard .kind-door{border-color:rgba(232,165,74,.55);background:rgba(232,165,74,.08)}
.roomboard .rb-stay{align-self:center;background:none;border:0;color:var(--muted);text-decoration:underline;
  font-size:14px;min-height:44px;cursor:pointer;padding:0 12px}
.roomboard .rb-stay[hidden]{display:none}
.roomboard .ri-head{color:var(--accent);font:clamp(12px,3.4vw,14px) Georgia,serif;letter-spacing:.12em;text-transform:uppercase}
.roomboard .kind-keys .rb-mark{display:none}
/* THE TWO HEADINGS ARE THE SCREEN'S TWO QUESTIONS — what is here, and where on — so they are centred and
   bold, a size above the tiles' own names; the marks that answer at a glance (done, a fight) are bigger. */
.roomboard .ri-head{text-align:center;font-weight:bold;font-size:clamp(14px,3.9vw,17px)}
.roomboard .kind-done .rb-mark,.roomboard .kind-fight .rb-mark{width:34px;height:34px;font-size:28px}
.roomboard .kind-fight .rb-mark svg{width:28px;height:28px}
.roomboard #rbClose{position:absolute;top:4px;right:6px;width:34px;height:34px;font-size:18px}
.look.full.roomboard.browse{padding-top:24px}
.waysmap .ways-board{display:block;margin:10px auto 0;min-height:44px;padding:0 16px;border-radius:999px;
  border:1px solid var(--accent);background:transparent;color:var(--bright);font:14px Georgia,serif;cursor:pointer}
.waysmap .ways-board[hidden]{display:none}

/* A deed missed here, offered again: one quiet line under its label. */
#lookActs button small.again{display:block;margin-top:2px;font-size:clamp(12px,3.2vw,13px);color:var(--accent);letter-spacing:.04em}

/* THE VEIL OF A STEP (`travelVeil` in location.js): the whole page dims, and footsteps cross it left
   to right, one after another, while the room underneath is swapped. */
#travelVeil{position:fixed;inset:0;z-index:60;pointer-events:none;opacity:0;
  background:radial-gradient(ellipse at 50% 55%, rgba(14,10,30,.88), rgba(11,7,22,.97) 70%);
  transition:opacity .28s ease}
#travelVeil.on{opacity:1}
#travelVeil.off{opacity:0;transition:opacity .45s ease}
#travelVeil .steps{position:absolute;left:0;right:0;top:50%;height:80px;transform:translateY(-50%)}
#travelVeil .steps i{position:absolute;top:0;width:18px;height:30px;opacity:0;
  left:calc(12% + var(--i) * 14%);transform:rotate(90deg)}
#travelVeil .steps i:nth-child(even){top:24px}
#travelVeil .steps i svg{width:100%;height:100%;fill:var(--accent)}
#travelVeil.on .steps i{animation:krag-step .5s ease-out forwards;animation-delay:calc(var(--i) * .12s)}
@keyframes krag-step{0%{opacity:0;scale:.6}40%{opacity:.85;scale:1}100%{opacity:.35;scale:1}}
@media (prefers-reduced-motion: reduce){
  #travelVeil.on .steps i{animation:none;opacity:.5}
}

/* THE BOARD'S TILES TAKE WHAT THEY NEED, AND «STAY» SITS RIGHT UNDER THEM. The first section grew into
   the whole column and the tiles stopped at their ceiling, so the leftover height opened up between
   the last tile and the button it belongs to; the slack now goes below it. The button is a quiet pill
   rather than an underlined word — still the least of the choices, but findable. */
.roomboard .rb-sec:first-child{flex:0 1 auto}
.roomboard .rb-sec:first-child .rb-tiles{flex:0 1 auto}
.roomboard .rb-sec:first-child .rb-tile{flex:0 1 auto;height:clamp(46px,11dvh,104px)}
.roomboard .rb-sec:first-child .rb-tiles.two{grid-auto-rows:clamp(46px,11dvh,104px)}
.roomboard .rb-on{margin-top:auto}
.roomboard .rb-stay{text-decoration:none;color:var(--ink);background:var(--panel);border:1px solid var(--line);
  border-radius:999px;padding:0 20px;font-size:15px;margin-top:2px}
.roomboard .rb-stay:hover{border-color:var(--accent)}
/* The keys tile only counts; nothing happens on it, so it does not look pressable. */
.roomboard .kind-keys{cursor:default}

/* ==== HOW IT LOOKS: the storybook or the epic pictures ====
   A row of two cards. On a phone it is swiped and the card in the middle is the choice; on a desk
   (wide enough for both) the cards sit side by side and the one pressed is the choice. Each card is
   that adventure's first room in that set, with the hero in the corner. The step never scrolls up
   and down: the picture is what gives way. */
.stylestep{display:flex;flex-direction:column;gap:10px;min-height:0;overflow:hidden}
.stylestep .styletrack{flex:1 1 auto;min-height:0;display:flex;gap:12px;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;padding:4px 8%;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.stylestep .styletrack::-webkit-scrollbar{display:none}
.stylestep .stylecard{flex:0 0 84%;scroll-snap-align:center;display:flex;flex-direction:column;gap:6px;min-height:0;
  text-align:left;padding:8px 8px 12px;border-radius:16px;border:2px solid var(--line);background:var(--panel2);
  color:var(--ink);cursor:pointer;transition:border-color .2s,box-shadow .2s,opacity .2s;opacity:.72}
.stylestep .stylecard.on{border-color:var(--accent);box-shadow:0 0 0 3px rgba(232,165,74,.18);opacity:1;background:var(--panel)}
.stylestep .styleplate{position:relative;flex:1 1 auto;min-height:120px;border-radius:11px;overflow:hidden;background:var(--panel2)}
.stylestep .styleplate .scene{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.stylestep .styleplate .hero{position:absolute;right:8px;bottom:8px;width:30%;max-width:120px;aspect-ratio:2/3;
  object-fit:cover;border-radius:9px;border:2px solid var(--accent);box-shadow:0 4px 14px rgba(0,0,0,.5)}
.stylestep .stylecard b{font-family:Georgia,serif;font-weight:normal;font-size:clamp(19px,5vw,22px);
  color:var(--bright);padding:2px 6px 0;letter-spacing:.02em}
.stylestep .stylecard > span:last-child{font-size:clamp(14px,3.8vw,15px);line-height:1.4;padding:0 6px;color:var(--ink);opacity:.9}
.stylestep .stylefoot{display:flex;flex-direction:column;align-items:center;gap:4px}
.stylestep .styledots{display:flex;gap:8px}
.stylestep .styledots i{width:9px;height:9px;border-radius:50%;background:var(--line);transition:background .2s}
.stylestep .styledots i.on{background:var(--accent)}
.stylestep .styleswipe{font-size:13px;color:var(--muted);letter-spacing:.04em}
.stylestep .stylenote{font-size:clamp(13px,3.5vw,14px);line-height:1.4;color:var(--muted);text-align:center;padding:0 6px 4px}
/* A DESK HAS ROOM FOR BOTH: no swipe, no dots, the choice is the card pressed (or moved with ← →). */
@media (min-width:720px){
  .stylestep .styletrack{overflow:visible;justify-content:center;padding:4px 0;scroll-snap-type:none}
  .stylestep .stylecard{flex:0 1 46%;max-width:520px}
  .stylestep .stylecard:hover{opacity:1}
  .stylestep .styledots,.stylestep .styleswipe{display:none}
}
body.device .stylestep .styletrack{overflow-x:auto;justify-content:flex-start;padding:4px 8%;scroll-snap-type:x mandatory}
body.device .stylestep .stylecard{flex:0 0 84%;max-width:none}
body.device .stylestep .styledots,body.device .stylestep .styleswipe{display:flex}
