/* Ristikkokone /v2/. Layout: the grid fills the screen. Phone: vertical scroll + clue bar + keyboard at the bottom; tablet and desktop: whole grid, info in a side panel on demand. Based on prototype D. */
:root {
  --paper:#f4f7f8; --sheet:#fff; --ink:#15212b; --muted:#5a6a76; --line:#23313d; --rule:#d5dee2;
  --clue-bg:#a9c1cc; --clue-ink:#0e1b24; --dead:#33444f; --cellbg:#fff; --reveal:#b8501a; --word-hi:#fbe6a0; --cell-hi:#f2c440;
  --pen:#1d2a36; --pencil:#8a97a1; --locked:#1b4e9b; --bad:#c0392b; --good:#2b7a4b; --accent:#1b4e9b; --accent-ink:#fff; --key-bg:#e3eaed; --halo:#fff;
  --block-bg:#23313d; --block-ink:#fff;
  /* the photo clue's arrow sits on a photo, so it is the same yellow with a dark edge in both themes */
  --photo-arrow:#ffd84d; --photo-arrow-edge:#15212b;
  /* the logo's tile (decision 22.10.2026, scripts/brand/wordmark.js): one blue on every background, so not redefined
     for the dark theme; the UI's --accent stays its own */
  --brand:#2a67c8;
  --f-ui:"Archivo","Helvetica Neue",Arial,sans-serif; --f-hand:"Patrick Hand","Segoe Print",cursive;
}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){
  --paper:#0f161b; --sheet:#151f26; --ink:#e5edf1; --muted:#93a5b1; --line:#6d8796; --rule:#24333d;
  --clue-bg:#3e5a6a; --clue-ink:#eef4f7; --dead:#05080a; --cellbg:#1d2a33; --reveal:#f39257; --word-hi:#3f3610; --cell-hi:#8a6c0f;
  --pen:#e5edf1; --pencil:#6f7f8a; --locked:#8db7ff; --bad:#ff7b6b; --good:#5cc28a; --accent:#8db7ff; --accent-ink:#0b1115; --key-bg:#22313b; --halo:#0b1115;
  --block-bg:#2b3f4c; --block-ink:#e5edf1; color-scheme:dark; } }
:root[data-theme="dark"]{
  --paper:#0f161b; --sheet:#151f26; --ink:#e5edf1; --muted:#93a5b1; --line:#6d8796; --rule:#24333d;
  --clue-bg:#3e5a6a; --clue-ink:#eef4f7; --dead:#05080a; --cellbg:#1d2a33; --reveal:#f39257; --word-hi:#3f3610; --cell-hi:#8a6c0f;
  --pen:#e5edf1; --pencil:#6f7f8a; --locked:#8db7ff; --bad:#ff7b6b; --good:#5cc28a; --accent:#8db7ff; --accent-ink:#0b1115; --key-bg:#22313b; --halo:#0b1115;
  --block-bg:#2b3f4c; --block-ink:#e5edf1; color-scheme:dark; }
*{box-sizing:border-box} html,body{height:100%}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{background:var(--paper);color:var(--ink);font-family:var(--f-ui);font-size:15px;-webkit-tap-highlight-color:transparent;overflow:hidden}
button{font:inherit;color:inherit;cursor:pointer}
button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
[hidden]{display:none!important}

.app{height:100%;display:flex;flex-direction:column;padding-inline:10px;}
.app.phone{padding-inline:4px}
.app{padding-block:6px calc(6px + env(safe-area-inset-bottom,0px));gap:6px}
.top{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
/* The wordmark KUVA [tile] RISTIKOT (decision 22.10.2026, scripts/brand/wordmark.js; test/brand.test.js keeps these
   values equal to it): Archivo 700 at normal width, the tile 0.74em with 0.12em on each side, centred on the cap
   height. 18px keeps the width and height of the earlier Kuva▸ristikot at 17px, so the phone header keeps its lines.
   The corner block without a photo writes it too (src/v2/app.js blockEl), in the block's own light ink. */
.top h1{margin:0;font-size:18px;font-weight:700;text-transform:uppercase;font-stretch:100%;letter-spacing:.01em;white-space:nowrap;color:var(--ink)}
.wm-tile{display:inline-block;width:.74em;height:.74em;margin:0 .12em;vertical-align:-.026em}
.wm-tile rect{fill:var(--brand)}
/* The beta label (decision 19.10.2026): a small tag on the name's top-right corner, out of the flow, so the name takes
   the room it took before and the header (two or three lines on a phone) keeps its lines. */
.top h1{position:relative}
.top h1 .beta{position:absolute;right:-7px;top:-11px;font-size:9px;font-weight:700;font-stretch:100%;letter-spacing:.06em;line-height:12px;color:var(--muted);background:var(--paper);border:1px solid var(--rule);border-radius:3px;padding:0 3px}
.badge{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;font-weight:600;border:1px solid var(--rule);border-radius:4px;padding:2px 6px}
.spacer{flex:1}
.btn{border:1.5px solid var(--line);background:var(--sheet);border-radius:6px;padding:6px 10px;font-size:13px;font-weight:600;white-space:nowrap}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.btn:disabled{opacity:.4;cursor:default}
.btn.on{background:var(--line);color:var(--sheet)}

.main{flex:1 1 0;min-height:0;display:flex;gap:10px}
.gridwrap{flex:1 1 0;min-width:0;min-height:0;position:relative;display:flex;align-items:flex-start;justify-content:center;overflow:hidden}
.scroller{width:100%;height:100%;overflow-y:auto;overflow-x:hidden;touch-action:pan-y;scrollbar-width:thin}
.board{--cs:40px;display:grid;grid-template-columns:repeat(var(--w),var(--cs));gap:1px;background:var(--line);border:2px solid var(--line);user-select:none;margin:0 auto;width:max-content}
.c{width:var(--cs);height:var(--cs);background:var(--cellbg);position:relative;display:flex;align-items:center;justify-content:center;cursor:pointer}
/* Given and revealed letters differ from the player's own by colour only (same font and size,
   feedback 12.10.2026); pencil letters are smaller and italic on purpose. */
.c.L .ch{font-family:var(--f-hand);font-size:calc(var(--cs)*.64);line-height:1;color:var(--pen)}
.c.L.pencil .ch{color:var(--pencil);font-size:calc(var(--cs)*.5);font-style:italic}
.c.L.locked .ch{color:var(--locked)}
.c.L.inword{background:var(--word-hi)} .c.L.cur{background:var(--cell-hi)}
.c.L.wrong{box-shadow:inset 0 0 0 3px var(--bad)}
.c.L.reject{animation:rej .35s}
@keyframes rej{0%,100%{background:var(--cell-hi)}50%{background:var(--bad)}}
.c.Q{background:var(--clue-bg);color:var(--clue-ink);flex-direction:column;align-items:stretch;justify-content:stretch;overflow:hidden}
.c.Q.dead{background:var(--dead);cursor:default}
.c.L.revealed .ch{color:var(--reveal)}
/* On the cursor (its cell and its slot in the clue bar) the coloured letters and the wrong cell's frame carry a thin
   halo: the cursor colour of a tinted page cannot keep their contrast for every hue (src/display.js PAIRS), the halo
   can, and contrast is measured against it. */
.c.L.cur.locked .ch,.c.L.cur.revealed .ch,.slot.cur.locked,.slot.cur.revealed{text-shadow:0 0 1px var(--halo),0 0 1px var(--halo),0 0 2px var(--halo),0 0 2px var(--halo)}
.c.L.cur.wrong{box-shadow:inset 0 0 0 3px var(--bad),inset 0 0 0 5px var(--halo)}
.slot.revealed{color:var(--reveal)}
.c.Q.corner{background:var(--line)}
/* Corner block: the unused clue cells at the top-left drawn as one cell (title, level, date of a
   daily puzzle). It spans its rows and columns and is a size container: the text scales with the
   block (container units; the cell size is the fallback). */
.c.Q.block{width:auto;height:auto;min-width:0;min-height:0;background:var(--block-bg);color:var(--block-ink);cursor:default;
  align-items:center;justify-content:center;container-type:size}
.c.Q.block .bi{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.2em;text-align:center;
  font-size:calc(var(--cs)*.24);font-size:clamp(7px,min(9cqw,19cqh),30px)}
.c.Q.block .bt{font-weight:700;text-transform:uppercase;font-stretch:100%;letter-spacing:.01em;line-height:1.05;white-space:nowrap}
.c.Q.block .bl,.c.Q.block .bd{font-size:.82em;font-weight:600;line-height:1.1;opacity:.9;white-space:nowrap}
.c.Q.block .bd{font-variant-numeric:tabular-nums}
.q{flex:1 1 0;min-height:0;display:flex;align-items:center;justify-content:center;text-align:center;position:relative;padding:1px 2px;font-size:calc(var(--cs)*.2);font-stretch:68%;font-weight:600;line-height:1.02;overflow:hidden;word-break:break-word;hyphens:auto}
.q+.q{border-top:1px solid var(--line)}
.q.active{background:var(--cell-hi);color:var(--ink)}
.q.done{color:var(--muted)}
.q span{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.q .img{font-size:calc(var(--cs)*.5);line-height:1}
.q .ar{position:absolute;width:0;height:0}
/* Small cells (phone): clue text is drawn tiny and truncated, images slightly larger;
   the full clue is read from the clue bar. Small edge arrows show the direction. */
.board.compact .q{font-size:calc(var(--cs)*.22);padding:0 1px;letter-spacing:-.01em}
.board.compact .c.Q.two .q span:not(.img){-webkit-line-clamp:2}
.board.compact .q .img{font-size:calc(var(--cs)*.55)}

.side{width:280px;flex:0 0 auto;border-left:1px solid var(--rule);padding-left:10px;display:flex;flex-direction:column;gap:10px;overflow:auto;font-size:14px}
.side h2{margin:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.side ul{margin:0;padding-left:18px;line-height:1.4}
.side .stat{display:flex;justify-content:space-between;border-bottom:1px dotted var(--rule);padding:3px 0}

.bottom{display:flex;flex-direction:column;gap:6px}
.cluebar{background:var(--sheet);border:2px solid var(--line);border-radius:8px;padding:6px 8px;display:flex;align-items:center;gap:8px;min-height:64px}
.cluebar .pic{width:56px;height:56px;display:flex;align-items:center;justify-content:center;font-size:40px;line-height:1;flex:0 0 auto}
.cluebar .pic:empty{display:none}
.cluebar .t{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.cluebar .meta{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;font-weight:600;display:flex;gap:8px}
/* Pencil mode is visible where the player looks: dashed clue bar and a label (feedback 13.10.2026). */
.cluebar.pencil{border-style:dashed;border-color:var(--pencil)}
.cluebar .meta .pen-on{color:var(--ink);background:var(--key-bg);border-radius:4px;padding:0 5px}
.cluebar .text{font-size:19px;font-weight:700;font-stretch:92%;line-height:1.1}
.cluebar .text.small{font-size:15px;font-weight:600;color:var(--muted)}
.cluebar .alt{font-size:12.5px;color:var(--muted);line-height:1.2}
/* The cell span is a line-clamped box; its inner text and blank spans stay inline (`.q span` would make them boxes). */
.q .ct,.q .blank{display:inline;overflow:visible}
.q .blank,.cluebar .blank{white-space:nowrap}
.slots{display:flex;gap:3px;flex-wrap:wrap}
.slot{width:22px;height:26px;border:1.5px solid var(--line);border-radius:3px;display:flex;align-items:center;justify-content:center;font-family:var(--f-hand);font-size:19px;line-height:1;background:var(--cellbg)}
.slot.cur{background:var(--cell-hi)}
.slot.locked{color:var(--locked)}
.slot.pencil{color:var(--pencil);font-style:italic}
.cluebar .nav{display:flex;flex-direction:column;gap:4px}
.cluebar .nav button{border:1.5px solid var(--line);background:transparent;border-radius:6px;width:34px;height:28px;font-size:16px}
.tools{display:flex;gap:6px}
.tools .btn{flex:1 1 0;min-width:0;padding:7px 4px;font-size:12.5px;text-align:center}
.kbd{display:flex;flex-direction:column;gap:5px}
.kbd .row{display:flex;gap:4px;justify-content:center}
.kbd button{flex:1 1 0;min-width:0;max-width:46px;height:42px;border:0;border-radius:5px;background:var(--key-bg);font-weight:700;font-size:17px}
.kbd button.wide{max-width:70px;flex-grow:1.6;font-size:14px}
.kbd button.on{background:var(--line);color:var(--sheet)}
.kbd button:active{background:var(--cell-hi)}
/* The stuck hint (app.js): the ⌫ key stands out while the hint shows, with a short pulse (none with reduced motion). */
.kbd button.hint{background:var(--accent);color:var(--accent-ink);animation:hint-pulse .6s ease-in-out 3}
@keyframes hint-pulse{50%{transform:scale(1.12);box-shadow:0 0 0 3px var(--cell-hi)}}

/* desktop / tablet: clue bar and tools side by side */
.app.wide .bottom{flex-direction:row;align-items:stretch;gap:8px}
.app.wide .cluebar{flex:1 1 0;min-height:52px}
.app.wide .tools{flex:0 0 auto;flex-direction:column;justify-content:center}
.app.wide .tools .btn{flex:0 0 auto}
.app.wide .kbd{flex:0 0 auto;justify-content:center}
.app.wide .kbd button{width:34px;height:34px;font-size:14px;flex:0 0 auto}
.app.wide .kbd button.wide{width:52px}

.minimap{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);flex:0 0 auto}
.minimap i{display:block;width:3px;height:3px;background:var(--cellbg)}
.minimap i.q{background:var(--clue-bg)} .minimap i.f{background:var(--pen)} .minimap i.v{outline:1px solid var(--accent)}

.modal-back{position:fixed;inset:0;background:rgba(10,16,20,.55);display:flex;align-items:center;justify-content:center;padding:16px;z-index:10}
.modal{background:var(--sheet);border:2px solid var(--line);border-radius:10px;max-width:380px;width:100%;padding:20px;display:flex;flex-direction:column;gap:12px}
.modal h2{margin:0;font-size:22px;font-weight:800;text-transform:uppercase;font-stretch:112%}
.modal .row{display:flex;gap:8px;flex-wrap:wrap}
.modal .row .btn{flex:1 1 auto}
/* Dialog content (formerly inline style attributes; the Content-Security-Policy allows no inline styles). */
.modal .msg{margin:0;font-size:16px;line-height:1.4}
.modal .note{margin:0;color:var(--muted);font-size:14px}
.modal .col{display:flex;flex-direction:column;gap:8px}
.modal .col.loose{gap:10px}
.modal .field{display:flex;flex-direction:column;gap:4px;font-size:13px;font-weight:600}
.modal .input{font:inherit;padding:8px;border:1.5px solid var(--line);border-radius:6px;background:var(--cellbg);color:var(--ink)}
.modal .field .input{font-weight:400}
.modal textarea.input{resize:vertical}
.modal a.btn{text-decoration:none;text-align:center;color:inherit}
.toast{pointer-events:none;position:fixed;left:50%;bottom:calc(16px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);background:var(--line);color:var(--sheet);padding:8px 14px;border-radius:6px;font-weight:600;font-size:14px;z-index:11;max-width:calc(100% - 32px)}
@media (prefers-reduced-motion:reduce){*{animation:none!important}}

/* Arrows: straight ones are CSS triangles, bent ones small SVGs. The arrow sits on the edge the word leaves from. */
.q .ar.right{right:0;top:50%;margin-top:-3px;border-top:3px solid transparent;border-bottom:3px solid transparent;border-left:4px solid currentColor}
.q .ar.down{bottom:0;left:50%;margin-left:-3px;border-left:3px solid transparent;border-right:3px solid transparent;border-top:4px solid currentColor}
.q svg.ar{position:absolute;width:9px;height:9px;fill:none;stroke:currentColor;stroke-width:1.6}
.q svg.ar.down-right{bottom:0;left:2px}
.q svg.ar.right-down{right:0;top:2px}
.board.compact .q:has(.ar.right),.board.compact .q:has(.ar.right-down){padding-right:6px}
.board.compact .q:has(.ar.down),.board.compact .q:has(.ar.down-right){padding-bottom:6px}

.top .pid{font-size:12px;color:var(--muted);white-space:nowrap}
.loading{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;background:var(--paper);font-weight:600;color:var(--muted);z-index:2}
.loading .spin{width:28px;height:28px;border:3px solid var(--rule);border-top-color:var(--accent);border-radius:50%;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.levels{display:flex;flex-direction:column;gap:8px}
.levels .btn{padding:12px;font-size:16px;text-align:left;display:flex;justify-content:space-between;align-items:center;gap:8px}
/* a level's description wraps on a phone instead of running past the button */
.levels .btn small{font-weight:400;color:inherit;opacity:.75;white-space:normal;text-wrap:balance;text-align:right}
.score{display:grid;grid-template-columns:1fr auto;gap:4px 12px;font-size:15px}
.score b{text-align:right}
.score .total{border-top:1.5px solid var(--line);padding-top:4px;font-size:18px}
.side .small{font-size:12px;color:var(--muted);line-height:1.4}
.side .small a{color:inherit;text-decoration:underline}

/* emoji pictures: colour glyphs also where a symbol defaults to text presentation */
.q .img,.cluebar .pic{font-variant-emoji:emoji;font-family:"Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif}
/* Info panel: on a phone it covers the grid instead of squeezing it (feedback 12.10.2026);
   "Takaisin ristikkoon" and Escape close it. Same query as SIDE_OVERLAY in app.js. */
.main{position:relative}
@media (max-width:700px), (max-height:500px) and (pointer:coarse){
  .side{position:absolute;inset:0;width:auto;z-index:5;background:var(--paper);border-left:0;padding:4px 2px 12px}
  .app.side-open .bottom{display:none} /* clue bar and keyboard would type into the covered grid */
}
/* With the on-screen keyboard at the bottom, messages show at the top so they never cover keys. */
.app.kbd-on ~ .toast{bottom:auto;top:calc(12px + env(safe-area-inset-top,0px))}

/* Photo clue (decision 16.10.2026): an AI photo fills the corner block and clues one word that starts next to it;
   the arrow on the block's edge points at the word's first cell. The level and the date move to the header. */
.c.Q.block.photo{padding:0;overflow:hidden}
.c.Q.block .ph{position:absolute;inset:0;width:100%;height:100%;padding:0;margin:0;border:0;background:var(--block-bg);cursor:zoom-in;display:block}
.c.Q.block .ph img{display:block;width:100%;height:100%;object-fit:cover;object-position:center}
/* the selection ring is drawn over the photo */
.c.Q.block.photo.active::after{content:"";position:absolute;inset:0;box-shadow:inset 0 0 0 3px var(--cell-hi);pointer-events:none}
.c.Q.block .q.pa{position:absolute;flex:none;overflow:visible;width:calc(var(--cs)*.46);height:calc(var(--cs)*.46);min-width:14px;min-height:14px;padding:0;background:none;border:0;cursor:pointer;z-index:1}
.c.Q.block .q.pa.right{transform:translate(-100%,-50%)}
.c.Q.block .q.pa.down{transform:translate(-50%,-100%)}
.c.Q.block .q.pa svg{display:block;width:100%;height:100%;fill:var(--photo-arrow);stroke:var(--photo-arrow-edge);stroke-width:1.1;stroke-linejoin:round}
.c.Q.block .q.pa.active svg{fill:var(--cell-hi)}
.cluebar .pic .thumb{width:56px;height:56px;padding:0;border:1.5px solid var(--line);border-radius:6px;overflow:hidden;background:var(--block-bg);cursor:zoom-in}
.cluebar .pic .thumb img{display:block;width:100%;height:100%;object-fit:cover}
/* Drawings (decisions 17–18.10.2026): black line art on white in place of the clue text, small in the clue cell and
   larger in the clue bar; a person drawing has the person badge (app.js personBadge, svg.pbadge: a bust in a circle) over its top-right corner,
   the same dark circle in both themes since the drawing stays white. Tapping it opens it large. */
.q .drw{display:block;position:relative;flex:none;overflow:visible;width:calc(var(--cs)*.8);height:calc(var(--cs)*.8);cursor:zoom-in}
.c.Q.two .q .drw{width:calc(var(--cs)*.46);height:calc(var(--cs)*.46)}
.c.Q.two .q .drw .pbadge{width:52%;height:52%}
.q .drw img{display:block;width:100%;height:100%;object-fit:contain;background:#fff;border-radius:2px}
.pbadge{position:absolute;top:0;right:0;width:46%;height:46%;pointer-events:none}
.pbadge .bb{fill:#15212b;stroke:#fff;stroke-width:1.6}
.pbadge .bu{fill:#fff}
.cluebar .pic .thumb.drw{position:relative;background:#fff}
.cluebar .pic .thumb.drw img{object-fit:contain}
.cluebar .pic .thumb.drw .pbadge{width:38%;height:38%;top:2px;right:2px}
.photo-view .photo-frame{position:relative}
.photo-view .photo-badge{position:absolute;top:10px;right:10px;width:17%;height:17%}
.photo-view .photo-badge:empty{display:none}
.photo-view .photo-badge .pbadge{position:static;display:block;width:100%;height:100%}
.photo-view.drawing img{background:#fff;object-fit:contain}
.photo-view{z-index:12}
.photo-view .photo-box{display:flex;flex-direction:column;align-items:center;gap:12px}
.photo-view img{display:block;width:min(88vw,74vh,512px);height:auto;aspect-ratio:1;object-fit:cover;border:2px solid var(--line);border-radius:8px;background:var(--block-bg);cursor:zoom-out}
.photo-view .btn{min-width:120px;font-size:15px;padding:8px 16px}

/* The photo's slide into the corner block (app.js photoIntro): an overlay image and a cover over the grid, both
   inside the board, which is their containing block (inside its outer border). */
.board{position:relative}
.board .intro-cover{position:absolute;inset:0;background:var(--paper);z-index:3;pointer-events:none}
.board .intro-fly{position:absolute;object-fit:cover;object-position:center;z-index:4;pointer-events:none;max-width:none;display:block}

/* The result screen of a photo puzzle: the photo on top, full width, about 40 % of the screen's height, and over its
   bottom edge, on a dark gradient, "Kuvassa" and the photo's word(s) in the hand font (written in lower case and
   shown in capitals, so a screen reader says the word instead of spelling it). */
.modal{max-height:100%;overflow-y:auto}
.modal .res-photo{position:relative;margin:-20px -20px 0;height:40vh;min-height:140px;max-height:420px;flex:0 0 auto;border-radius:8px 8px 0 0;overflow:hidden;background:var(--block-bg)}
.modal .res-photo img{display:block;width:100%;height:100%;object-fit:cover;object-position:center}
.modal .res-photo figcaption{position:absolute;left:0;right:0;bottom:0;padding:56px 18px 12px;background:linear-gradient(to top,rgba(10,15,19,.9),rgba(10,15,19,.65) 55%,rgba(10,15,19,0));color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.7);display:flex;flex-direction:column;gap:2px}
.modal .res-photo small{font-size:13px;font-weight:600;font-variant-caps:all-small-caps;letter-spacing:.08em;opacity:.9}
.modal .res-photo #res-photo-words{font-family:var(--f-hand);font-size:clamp(28px,9vw,40px);line-height:1;letter-spacing:.06em;text-transform:uppercase;overflow-wrap:anywhere}

/* Several words of one photo: each arrow carries its number (1, 2, 3 in reading order), a small yellow badge just
   inside the block next to the arrow. */
.c.Q.block .q.pa .pn{position:absolute;min-width:max(14px,calc(var(--cs)*.34));height:max(14px,calc(var(--cs)*.34));padding:0 2px;box-sizing:border-box;
  border-radius:999px;background:var(--photo-arrow);color:var(--photo-arrow-edge);border:1px solid var(--photo-arrow-edge);
  font:800 max(10px,calc(var(--cs)*.22))/1 var(--f-ui);display:flex;align-items:center;justify-content:center}
.c.Q.block .q.pa.right .pn{right:100%;top:50%;transform:translateY(-50%);margin-right:1px}
.c.Q.block .q.pa.down .pn{bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:1px}
.c.Q.block .q.pa.down.corner .pn{bottom:auto;left:auto;right:100%;top:50%;transform:translateY(-50%);margin:0 1px 0 0}
.c.Q.block .q.pa.active .pn{background:var(--cell-hi)}

/* The photo while the puzzle is made (app.js showLoadingPhoto): where the grid will be (the frame is the grid's box
   inside the board's border), blurred and translucent; it sharpens and hands over to the slide. The images stay
   invisible until they are decoded and animated. The text is kept for screen readers, the spinner goes. */
.loading{overflow:hidden}
.loading .lp-frame{position:absolute;pointer-events:none}
.loading .lp-frame img{position:absolute;inset:0;width:100%;height:100%;max-width:none;display:block;object-fit:cover;object-position:center;opacity:0}
.loading.photo .spin{display:none}
.loading.photo #loading-text{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* A photo with empty cells next to it (generator 8): the block spans its bounding box and is clipped to its shape from
   the script (clip-path), so the photo has a step in its edge; the cells of the box beside the step are drawn over it.
   The selection ring follows the shape (an SVG of the outline, its outer half clipped away). */
.c.Q.block.photo.shaped.active::after{display:none}
.c.Q.block .ring{display:none;position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1;fill:none;stroke:var(--cell-hi);stroke-width:6px;vector-effect:non-scaling-stroke}
.c.Q.block.photo.active .ring{display:block}
.c.Q.block .ring path{vector-effect:non-scaling-stroke}
/* The alternative look (display.js EMPTY_STYLE 'dark'): the photo stays the rectangle, each empty cell is a plain dark
   cell, and the grid line between them shows. */
.c.Q.block.dark-empty{background:var(--line)}
.c.Q.block .be{position:absolute;width:var(--cs);height:var(--cs);background:var(--block-bg);pointer-events:none}

/* Feedback where the player is (decision 17.10.2026). "Palaute" in the header: a speech bubble and its label; on a
   narrow phone the label is left to screen readers and the tooltip, so the header keeps its lines and the grid its
   height. The flag in the clue bar's top line reports the selected word's clue; its negative margins keep the line's
   height, so the clue bar does not grow. */
.btn.icon{display:inline-flex;align-items:center;gap:5px}
.btn .ic{width:15px;height:15px;margin-block:-3px;flex:0 0 auto;fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round}
@media (max-width:399px){
  .top .btn.icon{padding-inline:8px}
  .top .btn.icon .lbl{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
}
.cluebar .meta{align-items:center}
.cluebar .flag{margin:-8px -4px -8px auto;width:30px;height:27px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--muted);display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.cluebar .flag svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.cluebar .flag:hover{color:var(--ink);background:var(--key-bg)}
/* "Millainen ristikko oli?" in the result dialog: three answers in a row, the chosen one pressed. */
.modal .rate{display:flex;flex-direction:column;gap:6px}
.modal .rate .row{flex-wrap:nowrap;gap:6px}
.modal .rate .btn{flex:1 1 0;min-width:0;padding:8px 4px;white-space:normal}
.modal .rate .btn[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--sheet)}
.modal .rate .btn:disabled{opacity:.6}
#res-rate-thanks{color:var(--good);font-weight:600}

/* The welcome note of a first visit (decision 20.10.2026, app.js showWelcome): small, inside the grid area, so it never
   covers the clue bar or the keyboard; not modal. It fades in as the photo lands in the corner block: over the top of
   the board, and on a phone at the bottom of the grid area, clear of the photo and the first word (their cells fill
   the phone's narrow top edge). The sentence takes a line of its own when the note wraps, the × closing the level
   line. The grid area is a stacking context of its own: the note stays over the slide's photo and under the info panel
   (which covers the grid on a phone) and the dialogs. */
.gridwrap{isolation:isolate}
.welcome{position:absolute;top:8px;left:50%;z-index:5;transform:translateX(-50%);width:max-content;max-width:min(calc(100% - 16px),640px);
  display:flex;flex-wrap:wrap;align-items:center;gap:2px 12px;padding:6px 6px 6px 12px;background:var(--sheet);color:var(--ink);
  border:1.5px solid var(--line);border-radius:8px;box-shadow:0 4px 16px rgba(10,16,20,.28);font-size:14px;line-height:1.3;
  animation:welcome-in .3s ease-out both}
@keyframes welcome-in{from{opacity:0}to{opacity:1}}
.welcome p{margin:0}
.welcome .wl-text{font-weight:600}
.welcome .wl-level{color:var(--muted);white-space:nowrap}
.welcome .wl-level b{color:var(--ink);font-weight:600}
.welcome .wl-change{border:0;background:none;padding:0;color:var(--accent);font-weight:700;text-decoration:underline;text-underline-offset:2px}
/* a 30 px target that does not make its line taller */
.welcome .wl-close{margin:-6px 0 -6px auto;width:30px;height:30px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--muted);font-size:22px;line-height:1;flex:0 0 auto}
.welcome .wl-close:hover{color:var(--ink);background:var(--key-bg)}
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media (max-width:500px){
  .welcome{top:auto;bottom:6px;max-width:calc(100% - 12px);gap:0 10px;padding:5px 4px 5px 10px;font-size:13px;line-height:1.25}
  .welcome .wl-text{flex:1 0 100%}
  .welcome .wl-name{display:none}
}
