/* ══════════════════════════════════════════════════════════════════════════════════════
   cad.css — THE CAD SURFACES' OWN STYLESHEET.

   WHY THIS FILE EXISTS AT ALL. Everything else in this app is a list or a form, and the
   shared team kit (tim-ui.css) is right for those: light paper, cards, generous rows. The
   pohjaeditori and the luonnos canvas are neither. They are a DRAWING and its instrument
   chrome, and dressing a drawing in form clothes is what produced the screen Markus
   photographed — a page with a picture in it instead of a CAD tool. So the two canvas
   screens get their own visual language here, and app.css keeps the project list, the
   editor form and the admin exactly as they were. Nothing in this file may leak out of
   #cadedit / #sketch (plus the two sheets those screens own).

   THE PALETTE IS THE SAME IN BOTH THEMES, and that is a decision, not an oversight. A
   drawing surface is near-black in every CAD tool there is, for the same reason a light
   table is not: white geometry and one annotation colour separate from it without either
   of them shouting. Repainting the drawing when the phone flips to light mode would mean
   the surveyor reads two different drawings depending on the time of day. The app AROUND
   these screens still follows the theme; the drawing does not.

   WHERE THE VALUES COME FROM: the published mockup "Tuotesuunnittelu — mobiilin
   CAD-näkymä" (2026-08-01), which is the approved spec. Canvas #0D0F12, geometry #E8E6E1,
   grid #1C2026, annotation #F7941D (Timanttiasennus orange, used ONLY as annotation ink —
   selection, handles, snap, the live value), dimensions #9AA0A8. Numbers are monospace,
   because in a CAD tool almost every string on screen IS data and data is read in columns.
   ══════════════════════════════════════════════════════════════════════════════════════ */

/* ── 1. the palette ──────────────────────────────────────────────────────────────────
   Declared once, scoped to the CAD screens and the two sheets they own. The second half
   re-points the KIT's own token names at it, so every shared component that lands inside
   a CAD screen (.btn, .inp, .seg, .lbl, a toast) comes along without a single rule of the
   kit being touched or duplicated. */
/* Scoped to the two screens AND to the .app while one of them is showing — the app
   header is a SIBLING of the screens, so it can only inherit the palette from .app.
   The two sheets are outside .app entirely and are named explicitly.

   …AND .rappu-block, which lives on the project EDITOR screen. That is deliberate, and it
   is also a BUG FIX. The koko-rappu carrier group was written straight into section 9 of
   this file using var(--cad-ann) / var(--cad-edge) / var(--cad-warn), but it renders
   inside #editor, where none of those names existed — every one of them was invalid at
   computed-value time, so the group shipped with NO left accent, NO cell borders, NO
   open-state fill, and an .elwarn that printed a "this angle table cannot be used" message
   in ordinary body ink. A named problem that does not look like a problem is exactly what
   §7 forbids, and it looked fine at 1280 because nothing there was missing enough to see.
   Naming the block here fixes all four at once — and it is the right scope on the merits
   too: what the block shows is DRAWING data (the type drawing's angle schedule and the
   rappu-level carriers), not this floor's form data, so it gets the drawing's language. */
.app[data-screen="cadedit"], .app[data-screen="sketch"], .rappu-block,
#cadedit, #sketch, #cePalSheet, #ceKoosteSheet {
  --cad-canvas:#0D0F12;      /* the drawing surface */
  --cad-panel:#15181C;       /* the sheet / the context row */
  --cad-field:#1B1F24;       /* an input, a segment cell, a rail button */
  --cad-edge:#262B32;        /* panel + control borders */
  --cad-edge-2:#2A3038;      /* the same, one step brighter (a control against a control) */
  --cad-geo:#E8E6E1;         /* geometry ink */
  --cad-grid:#1C2026;        /* the grid mesh */
  --cad-grid-maj:#242A32;    /* …and every fifth line */
  --cad-fill:#14171B;        /* a slab: the floor you are standing on */
  --cad-solid:#242931;       /* a solid body seen in section (the stair's hub) */
  --cad-grab:#3A4048;        /* the sheet's grab bar */
  --cad-grab-hi:#4A515A;
  --cad-ann:#F7941D;         /* ANNOTATION ONLY: selection, handles, snap, live value */
  --cad-dim:#9AA0A8;         /* dimensions */
  /* labels, disabled glyphs, the scale bar. LIFTED FROM THE MOCKUP'S #7D848D, and that is
     a correction rather than a re-decision: #7D848D measures 4.38:1 on --cad-field and
     3.82:1 on a selected row's orange tint — under AA on the two surfaces it is actually
     used on, and what it was failing to carry is data (the model rows' element count,
     every field label, the object list's millimetres). #8C939C is 5.34 / 4.65 and stays
     visibly below --cad-dim, so the three-step quiet < dim < ink hierarchy is unchanged. */
  --cad-quiet:#8C939C;
  --cad-ink:#C9CDD4;         /* control glyphs and segment text */
  --cad-ok:#4FA97E;
  --cad-warn:#F0B15A;
  --cad-bad:#E0685C;
  --cad-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  /* the peek detent's own height — the sheet, the scale bar, the banners and the JS that
     snaps between detents all measure from this ONE number */
  --ce-peek-h:58px;
  /* ONE 38 px context row. The kit's 66 px header is a brand bar with a subtitle; a CAD
     screen needs a rule with a name on it. Redefining the kit's own token is what makes
     app.css's `.screen:not(#login){padding-top:calc(var(--header-h) + var(--safe-top))}`
     produce 38 here without a second, more specific padding rule fighting it. */
  --header-h:38px;

  /* …and the kit's names, re-pointed. This is the whole reason a shared .btn or .seg
     inside the sheet needs no CAD-specific rule of its own. */
  --bg:var(--cad-canvas);
  --surface:var(--cad-panel);
  --surface-2:var(--cad-canvas);
  --elev:var(--cad-field);
  --card:var(--cad-panel);
  --input-bg:var(--cad-field);
  --text:var(--cad-geo);
  --text-2:var(--cad-dim);
  --line:var(--cad-edge);
  --line-strong:var(--cad-edge-2);
  --border:var(--cad-edge);
  --muted:var(--cad-dim);
  --primary:var(--cad-ann);
  --primary-d:var(--cad-ann);
  --on-primary:#0D0F12;
  --ok:var(--cad-ok);
  --okbg:rgba(79,169,126,.15);
  --draft:var(--cad-warn);
  --draftbg:rgba(240,177,90,.14);
  --info:var(--cad-ink);
  --infobg:rgba(201,205,212,.12);
  --danger:var(--cad-bad);
  --shadow:0 -10px 34px rgba(0,0,0,.5);
  --shadow-sm:0 1px 2px rgba(0,0,0,.5);
  --topbar:var(--cad-panel);
  --topbar-text:var(--cad-geo);
  color-scheme:dark;
  /* …AND THE INK ITSELF, not only the token behind it. `--text` above is a CUSTOM
     PROPERTY: re-pointing it changes what `var(--text)` resolves to in rules that MATCH
     these elements, and the kit's one `color` declaration (`body{color:var(--text)}`,
     tim-ui.css) matched BODY, where --text is still the light kit's #3b3d42. `color` then
     inherits down into this near-black sheet unchanged, so in light theme every string
     added here without a colour of its own — the §7 muutoskooste list among them — landed
     at 1.45:1 and was effectively invisible. One declaration closes the whole class:
     inheritance now starts from the CAD's own geometry ink. */
  color:var(--cad-geo);
}

/* ── OBJEKTIVÄRIT ovat :root-tasolla, EIVÄT yllä olevassa CAD-paletissa ──────
   Ne olivat siellä, ja se oli VIKA jonka selain näytti mutta yksikään testi ei.
   Paletti on rajattu valitsimeen `#cadedit, #sketch, #cePalSheet, #ceKoosteSheet`,
   mutta objektin lomake (#objSheet) elää PROJEKTINÄKYMÄSSÄ, joka ei ole yksikään
   niistä. Siellä `var(--cad-obj-sininen)` ei ratkennut miksikään, joten viisi
   värinappia piirtyivät läpinäkyvinä: valittavana oli viisi näkymätöntä ruutua.

   Oikea rajaus on tämä: objektin väri EI ole CAD-ruudun pinnan väri. Se on
   projektin dataa, joka näkyy lomakkeessa, kankaalla JA tulostetussa
   pöytäkirjassa — siis sovelluksen laajuinen. Sävyt eroavat myös VAALEUDELTAAN,
   koska pöytäkirja tulostetaan (objects.config.ts OBJECT_COLORS). */
:root {
  --cad-obj-sininen:#3b6ea5;
  --cad-obj-vihrea:#4f8a5b;
  --cad-obj-keltainen:#c9a227;
  --cad-obj-punainen:#a2453c;
  --cad-obj-harmaa:#6b7078;
}

/* ── 1b. the kit's THEME-QUALIFIED rules must not reach CAD ink ───────────────────────
   `[data-theme="dark"] .seg button.on{color:var(--primary)}` (app.css) is (0,3,1) and
   out-specifies every class rule in this file no matter which stylesheet loads last — and
   `--primary` is re-pointed to `--cad-ann` above, so a kit .seg landing on a CAD surface
   printed ORANGE TEXT ON AN ORANGE FILL: contrast 1.00, the label gone. That is the
   kiertosuunta control, the one field the product rules say must never be ambiguous (it
   has no default, so a chosen hand losing its label reads as nothing chosen).

   The CAD screens carry ids, so one id-qualified rule outranks any theme-qualified class
   rule the kit has or gains, in BOTH themes, for every kit control this file re-skins. */
#cadedit .seg button.on, #sketch .seg button.on,
#cePalSheet .seg button.on, #ceKoosteSheet .seg button.on {
  background:var(--cad-ann); border-color:var(--cad-ann); color:var(--cad-canvas);
}

/* ── 2. ONE context row, 38 px ────────────────────────────────────────────────────────
   The app header IS the context row — that is why these screens have never had one of
   their own (two stacked back buttons is a composition bug and it cost a whole row of a
   844 px phone). Here it is compacted to the mockup's 38 px and repainted in CAD ink:
   back · "Porrashuone A · Toistuva kerros" · ⋮. Nothing else sits above the drawing. */
.app[data-screen="cadedit"], .app[data-screen="sketch"] { background:var(--cad-canvas); }
.app[data-screen="cadedit"] .app-header,
.app[data-screen="sketch"] .app-header {
  --header-h:38px;
  min-height:calc(38px + var(--safe-top));
  padding:var(--safe-top) 6px 0;
  gap:4px;
  background:var(--cad-panel);
  color:var(--cad-geo);
  border-bottom:1px solid var(--cad-grid);
}
.app[data-screen="cadedit"] .context-title,
.app[data-screen="sketch"] .context-title {
  font-family:var(--cad-mono);
  font-size:11.5px;
  font-weight:500;
  letter-spacing:.03em;
  color:var(--cad-ink);
}
/* 38 wide × 36 tall: the row is 38 px INCLUDING its 1 px rule, so the tallest thing in it
   has to be 37 or less. A CAD context row cannot host a 44 px target and be 38 px tall —
   the mockup made the same trade, and the 44 px targets are on the rail, where the work is. */
.app[data-screen="cadedit"] .iconbtn-bare,
.app[data-screen="sketch"] .iconbtn-bare {
  width:38px; height:36px; min-height:36px; border-radius:6px;
  background:transparent; color:var(--cad-quiet);
}
.app[data-screen="cadedit"] .iconbtn-bare:hover,
.app[data-screen="sketch"] .iconbtn-bare:hover { background:rgba(255,255,255,.06); color:var(--cad-geo); }
.app[data-screen="cadedit"] .iconbtn-bare .ic,
.app[data-screen="sketch"] .iconbtn-bare .ic { width:17px; height:17px; }
/* THE TITLE GETS THE ROW. Two kit rules were spending it elsewhere: a flex:1 spacer that
   took half the free width, and (on phones) an absolutely-positioned, centred .ctx-block
   capped at 56 % — between them "Porrashuone A · Toistuva kerros" ellipsised at
   "…Toistuva kerr…". A centred title is a brand bar's habit; a CAD context row is a
   breadcrumb, so it starts after the back arrow and runs as far as it needs. */
.app[data-screen="cadedit"] .head-spacer,.app[data-screen="sketch"] .head-spacer{flex:0 0 0}
.app[data-screen="cadedit"] .context .ctx-block,
.app[data-screen="sketch"] .context .ctx-block{
  position:static;left:auto;top:auto;transform:none;text-align:left;max-width:none;flex:1;min-width:0;
}
/* the subtitle only ever repeated what the sheet below says, and there is no room for it.
   (The brand block and the app-switcher are already hidden for every in-project screen by
   app.css — one owner per rule.) */
.app[data-screen="cadedit"] .ctx-sub,.app[data-screen="sketch"] .ctx-sub{display:none}

/* ── 3. the screens ───────────────────────────────────────────────────────────────────
   NOTE: no bare `display:flex` on the id. An unconditional display on a .screen id beats
   the kit's `.screen{display:none}` and left the editor PAINTED OVER every other screen
   (it is position:absolute, inset:0) — the Piirustukset screen showed its toolbar through.
   `.screen.active` owns display; the id rule owns layout only. */
#cadedit.active{display:flex}
#cadedit{flex-direction:column;overflow:hidden;background:var(--cad-canvas)}
#sketch.active{display:flex}
#sketch{flex-direction:column;overflow:hidden;background:var(--cad-canvas)}

/* THE CANVAS IS THE APP. .ce-body fills what the 38 px row leaves, the canvas fills
   .ce-body, and the sheet floats OVER the canvas — so moving the sheet between detents
   never changes the canvas's height, and the drawing is never re-fitted by a form. */
.ce-body{flex:1;min-height:0;display:flex;position:relative;overflow:hidden}
.ce-canvas{position:relative;flex:1;min-width:0;background:var(--cad-canvas);touch-action:none;
  -webkit-user-select:none;user-select:none;overflow:hidden;cursor:crosshair}
.ce-canvas svg{display:block;width:100%;height:100%;color:var(--cad-geo)}
#cadedit.ce-locked .ce-canvas{cursor:grab}

/* ── 4. the floating chrome ───────────────────────────────────────────────────────────
   Nothing here is a ROW. Every cluster sits ON the canvas, so it costs the drawing no
   vertical space at all — and ceChromeRects/ceFitBand route the geometry AROUND the ones
   that swallow pointer events, so "Sovita" never parks a handle under a button. */
.ce-float{position:absolute;z-index:3;display:flex}

/* THE TOOL RAIL — right edge, three groups, 44 px targets: kumoa/tee uudelleen · mitat /
   lisää / piirrä · zoom. Undo and redo used to be TWO STACKED ROWS above the drawing;
   here they are two buttons in a column that costs no height. */
/* VERTICALLY CENTRED, and that is load-bearing rather than taste. ceFitBand routes the
   drawing around every cluster that swallows pointer events, and it picks the edge that
   leaves the most SCALE. A rail anchored at the top is ~384 px of the canvas's right-hand
   side, and for a squarish plan on a tall phone "cut the top 400 px" and "cut the right
   60 px" score the same scale — so the fit cut the top, and the drawing sat in the bottom
   half of a half-empty canvas. Centred, neither a top nor a bottom cut leaves a usable
   band, so the exclusion spends the 60 px of width it should always have spent. (It is
   also where the thumb is.) max-height keeps it inside a short landscape canvas. */
/* IT WRAPS BEFORE IT SHRINKS, AND IT NEVER SHRINKS — corrected 2026-08-07, task #68.
   Eight 44 px buttons plus their gaps and borders are 384 px tall. A phone in landscape is a
   352 px canvas, so `max-height` capped the rail at 328 — and every box in the chain was a
   flex item with the initial `flex-shrink:1`, while `.ce-tgrp`'s `overflow:hidden` and the
   rail's own `overflow-y:auto` resolve `min-height:auto` to ZERO. So flexbox did what it was
   told: it took the missing 56 px out of the buttons, and the whole rail rendered 44 × 37 —
   under the floor, on a building site, sometimes through a glove. `overflow-y:auto` never
   fired, because there was nothing left to overflow.
   `flex:0 0 auto` on both the group and the button is what makes the 44 real. The overflow
   then has to go somewhere, and WRAPPING is the answer rather than scrolling: a second
   column costs 52 px of a 844 px-wide canvas, where scrolling would hide the zoom group
   behind a gesture with no affordance. `wrap-reverse` puts the new column to the LEFT, so
   the rail stays anchored on the right edge it is positioned against — and ceChromeRects
   measures whatever shape it ends up, so the fit routes around the wider rail by itself.
   `overflow-y:auto` survives underneath as the last resort for a canvas too short for even
   one group. */
/* CENTRED IN WHAT THE SHEET LEAVES, not in the canvas — corrected 2026-08-07 (phone audit).
   `top:50%` measures the whole canvas, and the properties sheet is 46 % of it at the half
   detent and 68 % at the full one. So opening the sheet you were trying to type into
   swallowed the entire Zoomaus group: no Lähennä, no Loitonna, no Sovita, and pinch is off
   app-wide — there was NO WAY to bring the drawing back without first closing the sheet.
   `--ce-sheet-h` is the sheet's live height (cad.js publishes it on every detent change AND
   on every frame of the drag, so this tracks a half-open sheet too); the rail is centred in
   the band above it, and its ceiling is that band, so it WRAPS into a second 44 px column
   rather than scrolling or shrinking. Zero on the desktop, where the sheet is a side column
   that overlaps nothing — one variable, no breakpoint of its own. */
.ce-rail{right:10px;top:calc((100% - var(--ce-sheet-h, 0px)) / 2);transform:translateY(-50%);
  flex-direction:column;gap:8px;align-items:flex-end;
  flex-wrap:wrap-reverse;align-content:flex-end;
  max-height:calc(100% - var(--ce-sheet-h, 0px) - 24px);
  transition:top .26s cubic-bezier(.32,.72,0,1),max-height .26s cubic-bezier(.32,.72,0,1);
  overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none}
/* the sheet's own resting height, so the rail clears the peek row before anything is even
   selected — the same number `.ce-scalebar` and `.ce-banners` already sit above */
#cadedit{--ce-sheet-h:calc(var(--ce-peek-h) + var(--safe-bot))}
.ce-rail::-webkit-scrollbar{display:none}
.ce-tgrp{flex:0 0 auto;display:flex;flex-direction:column;background:rgba(20,23,27,.86);border:1px solid var(--cad-edge-2);
  border-radius:10px;overflow:hidden;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.ce-fbtn{flex:0 0 auto;width:44px;height:44px;min-width:44px;min-height:44px;
  display:grid;place-items:center;border:0;background:transparent;
  color:var(--cad-ink);cursor:pointer;transition:background .12s,color .12s}
.ce-fbtn + .ce-fbtn{border-top:1px solid var(--cad-edge)}
.ce-fbtn:hover{background:rgba(255,255,255,.06);color:var(--cad-geo)}
.ce-fbtn:disabled{color:var(--cad-quiet);opacity:.45;cursor:default}
.ce-fbtn .ic{width:19px;height:19px;stroke-width:1.7}
.ce-fbtn.flip .ic{transform:scaleX(-1)}
/* ON = ANNOTATION ORANGE, and that is the only thing orange means in this chrome */
.ce-fbtn.on{background:var(--cad-ann);color:var(--cad-canvas)}
.ce-fbtn.on .ic{stroke-width:2}
.ce-fbtn:focus-visible{outline:2px solid var(--cad-ann);outline-offset:-3px}

/* TARTUNTA + RUUDUKKO — one compact segmented control in the opposite corner, not a
   labelled dropdown. The step is three buttons you can see the state of; the old
   <select> looked like bold text and hid which step was live. */
.ce-snapseg{left:10px;top:12px;align-items:stretch;background:rgba(20,23,27,.86);
  border:1px solid var(--cad-edge-2);border-radius:10px;overflow:hidden;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.ce-snapseg .ce-fbtn{border-right:1px solid var(--cad-edge)}
.ce-seg{display:flex}
.ce-seg-b{min-width:44px;min-height:44px;padding:0 8px;border:0;background:transparent;color:var(--cad-quiet);
  font-family:var(--cad-mono);font-size:12.5px;font-weight:500;cursor:pointer;transition:background .12s,color .12s}
.ce-seg-b + .ce-seg-b{border-left:1px solid var(--cad-edge)}
.ce-seg-b:hover{color:var(--cad-geo)}
.ce-seg-b.on{background:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
.ce-seg-b:disabled{opacity:.4;cursor:default}
.ce-seg-b:focus-visible{outline:2px solid var(--cad-ann);outline-offset:-3px}
/* Vahvistettuna mitään ei voi sijoittaa: koko sijoitusryhmä pois, ei tyhjää pilleriä. */
#cadedit.ce-locked .ce-snapseg{display:none}
#cadedit.ce-locked .ce-rail .ce-tgrp.place{display:none}

/* SCALE BAR — bottom-left, its on-screen length a round number of mm (set by cad.js).
   pointer-events:none, so it costs the drawing nothing. */
.ce-scalebar{position:absolute;left:12px;z-index:3;pointer-events:none;display:flex;flex-direction:column;
  align-items:stretch;min-width:24px;bottom:calc(var(--ce-peek-h) + var(--safe-bot) + 12px)}
.ce-scale-num{font:500 10.5px/1 var(--cad-mono);color:var(--cad-quiet);margin-bottom:3px;text-align:center;
  font-variant-numeric:tabular-nums}
.ce-scalebar::after{content:"";height:7px;border:1px solid var(--cad-quiet);border-top:0}

/* BANNERS — the §7 alarms, ONE LINE EACH.

   THIS STRIP IS INK ON TOP OF THE DRAWING, so it is rationed like ink. It used to carry
   two full paragraphs — the stale banner's lead + its enumerated changes + its action, and
   the standing "Vertailukohta" caveat — which measured 314 × 297 px at 390 × 844: 30 % of
   the canvas, covering more than half of nineteen of the forty drawn shapes, roughly eight
   times the area of the three floating chips this iteration removed from the same surface.
   The sentences moved into the SHEET (ceEhdotusHtml / ceVertailukohtaHtml), which scrolls
   and is one tap away; the strip keeps the alarm and a chevron that opens the sheet on it.

   They clear the rail on the right and the peek sheet below, and they are pointer-events:
   none except the ones that carry a button. align-items:flex-start so a note is as wide as
   its own line, not as wide as the canvas. */
.ce-banners{position:absolute;left:10px;right:66px;z-index:3;display:flex;flex-direction:column;gap:6px;
  pointer-events:none;align-items:flex-start;bottom:calc(var(--ce-peek-h) + var(--safe-bot) + 38px)}
.ce-note{border-radius:4px;padding:8px 11px;font-size:12px;font-weight:600;line-height:1.4;
  background:rgba(21,24,28,.92);border:1px solid var(--cad-edge);color:var(--cad-ink);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.ce-note.lock{color:var(--cad-ok);border-color:color-mix(in srgb,var(--cad-ok) 45%,transparent)}
.ce-note.warn{color:var(--cad-warn);border-color:color-mix(in srgb,var(--cad-warn) 45%,transparent)}
.ce-note.err{color:var(--cad-bad);border-color:color-mix(in srgb,var(--cad-bad) 55%,transparent)}
/* VANHENTUNUT — the banner that has to stand out: a change to the plan makes a solved
   casting an EHDOTUS (§7), and it must not be drawn in the settled solution's ink. */
.ce-note.vanha{color:var(--cad-warn);border-color:color-mix(in srgb,var(--cad-warn) 55%,transparent);
  background:rgba(240,177,90,.1);pointer-events:auto}
/* every tappable note takes a finger, and wears its severity's tint */
.ce-note.warn.ce-note-tap{pointer-events:auto;background:rgba(240,177,90,.1)}
.ce-note.err.ce-note-tap{pointer-events:auto;background:rgba(224,104,92,.1)}
/* A VALIDATION NOTE KEEPS ITS FIRST TWO LINES ON THE DRAWING and the rest in the sheet.
   `validateScene`'s sentences run to ~330 characters — one of them measured 314 × 152 px
   parked over the plan — and the offending shape is already tinted red beside it, so the
   claim is identifiable from two lines and the reasoning is one tap away. */
.ce-note-tap.clamp{align-items:flex-start}
.ce-note-tap.clamp .ce-note-h{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;
  -webkit-line-clamp:2;line-clamp:2;overflow:hidden}
.ce-note-tap.clamp .ce-note-c{margin-top:3px}
/* VERTAILUKOHTA moved into the sheet (.ce-p-prov) — a standing fact is not an alarm, and
   139 px of standing prose on top of a 258 × 295 px drawing was the largest single thing
   covering the geometry on every plan that was NOT stale. `.ce-note.info` went with it. */
.ce-vertailukohta b{color:var(--cad-geo)}
.ce-note-sub{font-weight:500;opacity:.9;margin-top:3px}
.ce-note-list{margin:6px 0 0;padding-left:16px;font-weight:600}
.ce-note-list li{margin:1px 0}
/* THE TAPPABLE ONE-LINER. A 44 px target (it is the way into the sheet's own §7 block, so
   it is a control, not a label), the headline in its note's colour, the count quiet beside
   it, a chevron that says which direction the detail is in. max-width keeps a long headline
   from spanning the canvas; the ellipsis never eats the chevron. */
.ce-note-tap{display:flex;align-items:center;gap:8px;min-height:44px;max-width:100%;
  font:inherit;font-size:12px;font-weight:600;line-height:1.35;text-align:left;cursor:pointer;
  pointer-events:auto}
.ce-note-tap .ce-note-h{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ce-note-tap .ce-note-meta{flex:0 1 auto;min-width:0;font-weight:500;opacity:.82;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ce-note-tap .ce-note-c{width:14px;height:14px;flex:0 0 auto;opacity:.8}
.ce-note-tap:focus-visible{outline:2px solid var(--cad-ann);outline-offset:2px}
/* the kooste plate's own action: a §7 button, so it keeps the screen's 44 px floor rather
   than .btn.sm's ~38 */
.ce-kbtn{margin-top:7px;min-height:44px}
.ce-khuom-t{font-weight:700;color:var(--cad-geo);margin-top:6px}

/* HUD — the sketch canvas still floats its chips over the drawing (a stroke IS the
   readout there). The EDITOR's readout moved into the peek row, which is why #ceHud is
   gone from #cadedit: three chips over a quarter-screen drawing was ink on top of ink. */
.ce-hud{position:absolute;left:10px;top:12px;display:flex;flex-direction:column;gap:6px;
  pointer-events:none;align-items:flex-start;max-width:calc(100% - 216px);z-index:3}
.ce-dim{background:var(--cad-panel);border:1px solid var(--cad-edge);color:var(--cad-geo);
  font-family:var(--cad-mono);font-size:12.5px;border-radius:4px;padding:5px 9px}
.ce-snapchip{background:rgba(247,148,29,.14);color:var(--cad-ann);font-family:var(--cad-mono);
  font-size:11px;border-radius:3px;padding:3px 7px}
.ce-snapchip.centre{background:rgba(79,169,126,.16);color:var(--cad-ok)}

/* ── 5. the drawing itself ────────────────────────────────────────────────────────────
   Line weights are screen constants (cad.js multiplies them by mm-per-px). White
   geometry, a grid you can just see, dimensions in their own grey, and ORANGE ONLY where
   the app is annotating: selection, handles, snap lines, the axis datum. */
.ce-grid line{stroke:var(--cad-grid);stroke-width:1}
.ce-grid line.maj{stroke:var(--cad-grid-maj);stroke-width:1}
.ce-grid line.axis{stroke:var(--cad-ann);stroke-width:1;opacity:.34}
.ce-wall{fill:var(--cad-geo);opacity:.1;stroke:none}
.ce-slab{fill:var(--cad-fill);opacity:.9}
.ce-slab.open{opacity:.35}
.ce-roomline{fill:none;stroke:currentColor;stroke-width:2}
.ce-ovi-gap{fill:var(--cad-canvas);stroke:none}
.ce-jamb{stroke:currentColor;stroke-width:1.6}
.ce-obj rect{fill:none;stroke:currentColor;stroke-width:1.6;opacity:.85}
.ce-obj text{fill:currentColor;opacity:.6;font-family:var(--cad-mono)}
.ce-obj .ce-x{stroke:currentColor;stroke-width:1;opacity:.4}
.ce-obj.punch rect{stroke-width:2}
.ce-obj.pit rect{stroke-dasharray:7 4}
.ce-obj.whole rect{opacity:.4;stroke-dasharray:3 5}
.ce-kp line,.ce-kp circle{stroke:currentColor;stroke-width:1.6;fill:none}
.ce-fan{pointer-events:none}
.ce-fan-l{stroke:var(--cad-geo);stroke-width:1;opacity:.55}
.ce-fan-arc{fill:none;stroke:var(--cad-geo);stroke-width:1.4;opacity:.7}
/* VANHENTUNUT VIUHKA: the ends are right for THIS opening, but they are drawn under a
   solution chosen for another one — an EHDOTUS. A solid line means a record; this does not. */
.ce-fan.vanhentunut .ce-fan-l{stroke:var(--cad-warn);stroke-dasharray:6 5;opacity:.75}
.ce-fan.vanhentunut .ce-fan-arc{stroke:var(--cad-warn);stroke-dasharray:9 5;opacity:.9}
.ce-col rect{fill:var(--cad-solid);stroke:currentColor;stroke-width:2}
.ce-col circle{fill:var(--cad-solid);stroke:currentColor;stroke-width:2}
.ce-col .ce-x{stroke:currentColor;stroke-width:1;opacity:.5}
.ce-col.centred rect,.ce-col.centred circle{stroke:var(--cad-ok)}
.ce-col.bad rect,.ce-col.bad circle{stroke:var(--cad-bad);fill:rgba(224,104,92,.16)}
.ce-bad{fill:rgba(224,104,92,.16);stroke:var(--cad-bad);stroke-width:2}
.ce-vtx{fill:var(--cad-canvas);stroke:var(--cad-ann);stroke-width:2}
.ce-vtx.on{fill:var(--cad-ann)}
.ce-selbox{fill:none;stroke:var(--cad-ann);stroke-width:1.6;stroke-dasharray:6 4}
/* read-only: the selection still DRAWS (a reading aid), the grab affordances do not */
.ce-selbox.ro{stroke:var(--cad-dim);opacity:.7}
.ce-h{fill:var(--cad-ann);stroke:var(--cad-canvas);stroke-width:2}
.ce-snapline{stroke:var(--cad-ann);stroke-width:1.2;stroke-dasharray:5 5;opacity:.9}
.ce-snapline.centre{stroke:var(--cad-ok)}
/* MITAT — the server's dimension language (dim.ts) on the editing canvas: 45° ticks,
   endpoints, extension lines. Two levels: the room's W/D quiet, the selection's forward. */
.ce-dims{pointer-events:none}
.ce-dim-l,.ce-dim-tick{stroke:currentColor;fill:none}
.ce-dim-ext{stroke:currentColor;fill:none;opacity:.45}
.ce-dim-dot{fill:currentColor;stroke:none}
.ce-dim-t{fill:currentColor;font-family:var(--cad-mono);font-weight:500;
  paint-order:stroke;stroke:var(--cad-canvas);stroke-linejoin:round}
.ce-dim-g{color:var(--cad-dim)}
.ce-dim-g.ambient{opacity:.7}
.ce-dim-g.ambient .ce-dim-l,.ce-dim-g.ambient .ce-dim-tick{stroke-width:1}
.ce-dim-g.sel .ce-dim-l,.ce-dim-g.sel .ce-dim-tick{stroke-width:1.4}
.ce-dim-g.sel .ce-dim-t{fill:var(--cad-geo)}
.ce-dim-g.clear{color:var(--cad-dim)}
.ce-dim-g.clear .ce-dim-t{fill:var(--cad-dim)}
.ce-dim-g.size .ce-dim-t{fill:var(--cad-ann)}

/* ── 6. THE DRAG SHEET ────────────────────────────────────────────────────────────────
   Three detents, one gesture, and the canvas never moves.

   PEEK says what is selected (and, while you drag, what its live value is — that is where
   the three chips that used to cover the drawing went). HALF gives that thing's settings.
   FULL gives the rest. The sheet is position:absolute over the canvas, so switching detent
   does not resize .ce-canvas by a single pixel: the fit stays put, and a form can never
   push the drawing up.

   The grabber is a real <button>: it can be tabbed to, it announces its state, ↑/↓ move
   one detent, Enter/Space cycle. cad.js owns the pointer drag (touch AND mouse) and snaps
   to the NEAREST detent on release — no momentum, no fling, because a drawing that keeps
   moving after your thumb stops is a drawing you have to chase. */
.ce-sheet{position:absolute;left:0;right:0;bottom:0;z-index:4;display:flex;flex-direction:column;
  background:var(--cad-panel);border-top:1px solid var(--cad-edge);
  height:calc(var(--ce-peek-h) + var(--safe-bot));
  max-height:calc(100% - 88px);
  padding:0 14px;padding-bottom:var(--safe-bot);
  transition:height .26s cubic-bezier(.32,.72,0,1)}
.ce-sheet[data-detent="half"]{height:46%;box-shadow:var(--shadow)}
.ce-sheet[data-detent="full"]{height:68%;box-shadow:var(--shadow)}
.ce-sheet.dragging{transition:none}
/* The grabber: a 14 px band carrying the 38 × 4 bar the eye looks for. The PEEK ROW below
   it drags too, so the handle a thumb actually gets is the whole 58 px of the peek. */
.ce-grab{flex:0 0 auto;width:100%;height:14px;border:0;background:transparent;padding:0;cursor:grab;
  display:grid;place-items:center;touch-action:none}
.ce-grab:active{cursor:grabbing}
.ce-grab::before{content:"";display:block;width:38px;height:4px;border-radius:2px;background:var(--cad-grab);
  transition:background .12s}
.ce-grab:hover::before{background:var(--cad-grab-hi)}
.ce-grab:focus-visible{outline:2px solid var(--cad-ann);outline-offset:-2px;border-radius:4px}
/* the peek row — always there, with or without a selection, so the sheet is never a dead
   strip and the properties are always one tap away */
.ce-peek{flex:0 0 auto;display:flex;width:100%;align-items:center;gap:9px;min-height:44px;padding:0;
  border:0;background:transparent;color:var(--cad-geo);font:inherit;text-align:left;cursor:pointer;
  touch-action:none}
.ce-peek-t{font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cad-ann);border:1px solid rgba(247,148,29,.45);border-radius:3px;padding:2px 6px;flex:0 0 auto}
.ce-peek-m{flex:1;min-width:0;font-family:var(--cad-mono);font-size:13px;color:var(--cad-geo);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
/* THE LIVE VALUE, while a drag is in flight — the chip that used to float over the
   drawing. Same place, same row, orange because it is annotation. */
.ce-peek-m.live{color:var(--cad-ann)}
/* while a drag is live the value owns the row: the name pill steps aside (you know what
   you are dragging) and the value never shrinks before the snap chips do */
.ce-peek.live .ce-peek-t{display:none}
.ce-peek.live .ce-peek-m{flex:0 0 auto}
.ce-peek-snaps{flex:1 1 0;min-width:0;display:flex;gap:4px;overflow:hidden}
.ce-peek-snaps:empty{display:none}
/* THE CHIPS YIELD, THE VALUE DOES NOT. One snapped axis prints its name in full; two
   compete for what is left of the row and truncate, and the full name is still on the
   chip (title + accessible name). Note the DESKTOP row is the NARROW one — the sheet is a
   300 px side column there — so this cannot be a width media query. */
.ce-snapchip{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ce-snapchip-n{margin-left:5px}
.ce-peek-c{width:15px;height:15px;flex:0 0 auto;color:var(--cad-quiet);transition:transform .2s ease}
.ce-sheet[data-detent="half"] .ce-peek-c,.ce-sheet[data-detent="full"] .ce-peek-c{transform:rotate(180deg)}
/* the body: hidden at peek, scrolls at half and full */
.ce-pbody{display:none;flex:1;min-height:0;overflow-y:auto;padding:10px 0 14px;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.ce-sheet[data-detent="half"] .ce-pbody,.ce-sheet[data-detent="full"] .ce-pbody{display:block}
/* the peek row already names what is selected — the body must not say it twice */
.ce-sheet .ce-pbody > .ce-p-title{display:none}
/* the scale bar and the banners live where the expanded sheet lands: fold them away
   while it is open, and they are back the moment it collapses */
.ce-body:has(.ce-sheet[data-detent="half"]) .ce-scalebar,
.ce-body:has(.ce-sheet[data-detent="full"]) .ce-scalebar,
.ce-body:has(.ce-sheet[data-detent="half"]) .ce-banners,
.ce-body:has(.ce-sheet[data-detent="full"]) .ce-banners{opacity:0;transition:opacity .12s}

/* ── 7. what is IN the sheet ──────────────────────────────────────────────────────────
   Labels are small mono capitals (they are field names, i.e. data about data). Values are
   mono. Short pairs sit side by side — vertical space is the scarcest thing on this
   screen — and every number has a ± stepper, because a CAD user nudges 10 mm far more
   often than they retype 1900. */
.ce-p-title{font-family:var(--cad-mono);font-size:13px;letter-spacing:.04em;color:var(--cad-geo);margin-bottom:5px}
.ce-p-sub{font-size:11.5px;color:var(--cad-quiet);line-height:1.45;margin-bottom:12px}
.ce-p-note{font-size:11.5px;color:var(--cad-quiet);line-height:1.45;margin:10px 0 0}
.ce-p-warn{font-weight:600;line-height:1.45;color:var(--cad-warn)}
.ce-p-lock{display:inline-block;background:rgba(79,169,126,.16);color:var(--cad-ok);border-radius:3px;
  padding:3px 9px;font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;
  margin-bottom:9px}
.ce-p-refuse,.ce-p-prov{border-left:2px solid var(--cad-edge-2);padding-left:9px;color:var(--cad-quiet);font-size:11.5px;line-height:1.45}
/* the validation notes, in full — the canvas keeps two lines of each */
.ce-p-issue{font-size:12px;line-height:1.5;margin:0 0 8px;padding-left:9px;border-left:2px solid currentColor}
.ce-p-issue.warn{color:var(--cad-warn)}
.ce-p-issue.err{color:var(--cad-bad)}
.ce-p-prov b{color:var(--cad-geo)}
/* Field rhythm, tightened 2026-08-07. A property sheet on a phone is read in a glance and
   scrolled with a thumb; 10 + 6 px of air per field added a whole field's height to every
   four of them. The label is 9.5 px uppercase mono and needs less separation than a
   sentence would. */
.ce-f{margin-bottom:7px}
.ce-f .lbl{display:block;margin:0 0 3px;font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--cad-quiet);font-weight:500}
.ce-fgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px}
.ce-fgrid .ce-f{margin-bottom:0}
/* THE STEPPER. 44 × 44 targets either side of a 16 px monospace field (16 px is the floor:
   anything smaller and iOS zooms the canvas away the moment the field is focused). The
   buttons were 40 px WIDE — the brief asked for a ≥44 px target and 40 is not one, and a
   pair of them is the control a surveyor hits most often on this screen. Two 44s plus the
   10 px gap still leave 88 px of field in each half of a 390 px paired row. */
.ce-step{display:flex;align-items:stretch;border:1px solid var(--cad-edge-2);border-radius:8px;
  overflow:hidden;background:var(--cad-field)}
/* 44 WIDE, NOT 40, and it stays that way. Markus asked for smaller controls on 2026-08-07
   ("Inputs are pretty big") and narrowing these nudge buttons was the first thing I tried —
   tests/…  caught it: 44 px is the minimum touch target IN BOTH DIRECTIONS, and this app is
   used on a building site, sometimes with gloves. Density on this screen has to come from
   spacing and from prose, never from the size of the thing you have to hit. */
.ce-step button{width:44px;min-height:44px;flex:0 0 auto;background:transparent;border:0;color:var(--cad-ink);
  font-family:var(--cad-mono);font-size:17px;line-height:1;cursor:pointer;transition:background .12s}
.ce-step button:hover{background:rgba(255,255,255,.06);color:var(--cad-geo)}
.ce-step button:disabled{opacity:.35;cursor:default}
.ce-step button:focus-visible{outline:2px solid var(--cad-ann);outline-offset:-3px}
.ce-step .inp{flex:1;min-width:0;width:100%;min-height:44px;background:transparent;border:0;border-radius:0;
  color:var(--cad-geo);font-family:var(--cad-mono);font-size:16px;text-align:center;padding:0 2px;
  font-variant-numeric:tabular-nums}
.ce-step .inp:focus{outline:none;background:rgba(247,148,29,.1)}
.ce-step .inp:disabled{color:var(--cad-dim);-webkit-text-fill-color:var(--cad-dim);opacity:1}
.ce-f input[type=number]{-moz-appearance:textfield;appearance:textfield}
.ce-f input::-webkit-outer-spin-button,.ce-f input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
/* a plain (non-numeric) field, e.g. a free-text chooser */
.ce-f-sel .inp{width:100%;min-height:44px;font-size:16px;padding:11px 10px;background:var(--cad-field);
  border:1px solid var(--cad-edge-2);border-radius:8px;color:var(--cad-geo);font-family:var(--cad-mono)}
/* PORRASMALLI: A VERTICAL LIST, NOT A <select> AND NOT A SEGMENT.
   The names are long ("Timanttiporras 2 — 600") — a horizontal segment truncates them, and
   a native picker hides every alternative behind a tap. This switch recasts fifteen moulds,
   so the alternatives are exactly what has to be on screen. One row = one registry model,
   44 px+, its own second line carrying the registry's kind and element count. */
.ce-mlist{display:flex;flex-direction:column;gap:6px}
.ce-mrow{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;min-height:48px;
  padding:9px 12px;border:1px solid var(--cad-edge-2);border-radius:8px;background:var(--cad-field);
  color:var(--cad-ink);font:inherit;text-align:left;cursor:pointer;transition:border-color .12s,background .12s}
.ce-mrow:hover{border-color:var(--cad-ann)}
.ce-mrow-n{font-family:var(--cad-mono);font-size:13.5px;color:var(--cad-geo)}
.ce-mrow-s{font-size:11px;color:var(--cad-quiet);font-variant-numeric:tabular-nums}
.ce-mrow.on{border-color:var(--cad-ann);background:rgba(247,148,29,.13)}
.ce-mrow.on .ce-mrow-n{color:var(--cad-ann);font-weight:600}
.ce-mrow:focus-visible{outline:2px solid var(--cad-ann);outline-offset:2px}
/* A VERTICAL SEGMENT — yläpinta, nokka, kerroksen laji. Same reason as the model list: the
   labels are sentences ("Mosaiikkibetoni pintakerroksena"), not two-letter states. */
.ce-vseg{display:flex;flex-direction:column;gap:6px}
.ce-vseg button{width:100%;min-height:44px;padding:10px 12px;border:1px solid var(--cad-edge-2);
  border-radius:8px;background:var(--cad-field);color:var(--cad-ink);font-family:var(--cad-mono);
  font-size:12.5px;text-align:left;cursor:pointer}
.ce-vseg button:hover:not(:disabled){border-color:var(--cad-ann)}
.ce-vseg button.on{background:var(--cad-ann);border-color:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
.ce-vseg button:disabled{opacity:.45;cursor:default}
.ce-vseg button:focus-visible{outline:2px solid var(--cad-ann);outline-offset:2px}
/* a titled block inside the sheet — the sections the full detent adds */
.ce-sect{margin-top:16px;padding-top:13px;border-top:1px solid var(--cad-edge)}
.ce-sect-h{font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cad-quiet);margin-bottom:9px}
/* THE MODEL'S OWN NUMBERS. Monospace, tabular, and each cell says whose number it is. */
.ce-lgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}
.ce-lcell{background:var(--cad-field);border-radius:8px;padding:8px 10px;display:flex;flex-direction:column;gap:1px}
.ce-ln{font-family:var(--cad-mono);font-size:16px;font-variant-numeric:tabular-nums;color:var(--cad-geo)}
.ce-ll{font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--cad-quiet);line-height:1.3}
/* §7: a count that is an ASSUMPTION (or a project-specific exception) says so IN the cell.
   The tint is the same amber every unconfirmed thing on this screen wears. */
.ce-lcell.merkitty{background:rgba(240,177,90,.12)}
.ce-lm{font-family:var(--cad-mono);font-size:9px;letter-spacing:.08em;text-transform:uppercase;
  font-weight:700;color:var(--cad-warn);line-height:1.3;margin-top:2px}
/* OPEN CONFLICTS: the count is the way in, the topics are the registry's own strings. */
.ce-conf{margin-top:12px;border:1px solid color-mix(in srgb,var(--cad-warn) 40%,transparent);
  border-radius:8px;padding:9px 11px;color:var(--cad-warn);font-size:11.5px;line-height:1.45}
.ce-conf summary{cursor:pointer;font-weight:600;list-style:none;display:flex;align-items:center;gap:7px;
  min-height:26px}
.ce-conf summary::-webkit-details-marker{display:none}
/* the count is a SOLID chip, not amber-on-amber: it is the one number in this block and it
   had the worst contrast on the screen */
.ce-conf-n{font-family:var(--cad-mono);font-size:15px;background:var(--cad-warn);color:var(--cad-canvas);
  border-radius:4px;padding:1px 7px;font-variant-numeric:tabular-nums}
.ce-conf-l{margin:8px 0 0;padding-left:16px;color:var(--cad-ink)}
.ce-conf-l li{margin:3px 0}
/* THE PROPOSAL BLOCK — §7 at the top of the sheet's body, with its two acts.
   Warn ink, because a proposal is not a record; the two buttons are side by side and
   44 px, and the one the app would refuse is disabled with the refusal printed under it. */
.ce-ehdotus{border:1px solid color-mix(in srgb,var(--cad-warn) 55%,transparent);border-radius:8px;
  padding:11px 12px;margin-bottom:14px;background:rgba(240,177,90,.07)}
.ce-eh-t{font-family:var(--cad-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--cad-warn);font-weight:700;margin-bottom:5px}
.ce-ehdotus .ce-p-sub{margin-bottom:6px}
.ce-eh-acts{display:flex;gap:8px;margin-top:10px}
.ce-eh-b{flex:1;min-height:44px;padding:10px 12px;border-radius:8px;font-family:var(--cad-mono);
  font-size:12.5px;cursor:pointer;border:1px solid var(--cad-edge-2);background:var(--cad-field);
  color:var(--cad-ink)}
.ce-eh-b.vahvista{background:var(--cad-ann);border-color:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
.ce-eh-b:disabled{opacity:.4;cursor:default}
.ce-eh-b:focus-visible{outline:2px solid var(--cad-ann);outline-offset:2px}
/* the same state, one glyph, on the resting peek row */
.ce-peek-eh{flex:0 0 auto;font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.12em;font-weight:700;
  color:var(--cad-warn);border:1px solid color-mix(in srgb,var(--cad-warn) 55%,transparent);
  border-radius:3px;padding:2px 5px}
/* WHAT IS ON THIS FLOOR — the list that makes an object reachable without finding it on
   the drawing first. Row = name + the card list's own summary; × deletes. */
.ce-olist{display:flex;flex-direction:column;gap:5px}
.ce-orow{display:flex;align-items:stretch;gap:5px}
.ce-orow-b{flex:1;min-width:0;min-height:46px;display:flex;flex-direction:column;align-items:flex-start;
  justify-content:center;gap:1px;padding:7px 11px;border:1px solid var(--cad-edge-2);border-radius:8px;
  background:var(--cad-field);color:var(--cad-ink);font:inherit;text-align:left;cursor:pointer}
.ce-orow-b:hover:not(:disabled){border-color:var(--cad-ann)}
.ce-orow-b:disabled{opacity:.55;cursor:default}
.ce-orow.on .ce-orow-b{border-color:var(--cad-ann);background:rgba(247,148,29,.13)}
.ce-orow-n{font-family:var(--cad-mono);font-size:12.5px;color:var(--cad-geo)}
.ce-orow-m{font-family:var(--cad-mono);font-size:10.5px;color:var(--cad-quiet);font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.ce-orow-x{flex:0 0 auto;width:44px;min-height:46px;border:1px solid var(--cad-edge-2);border-radius:8px;
  background:transparent;color:var(--cad-quiet);font-size:19px;line-height:1;cursor:pointer}
.ce-orow-x:hover{color:var(--cad-bad);border-color:color-mix(in srgb,var(--cad-bad) 45%,transparent)}
/* which storeys use this plan — the chips decide WHICH solved fan the canvas draws */
.ce-stchips{display:flex;flex-wrap:wrap;gap:6px}
.ce-stchip{min-height:44px;padding:8px 12px;border:1px solid var(--cad-edge-2);border-radius:8px;
  background:var(--cad-field);color:var(--cad-ink);font-family:var(--cad-mono);font-size:12px;cursor:pointer}
.ce-stchip.on{background:var(--cad-ann);border-color:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
.ce-stchip:disabled{opacity:.45;cursor:default}
/* ── the FULL detent's own content ────────────────────────────────────────────────────
   `half` is what you CHANGE about this stair; `full` is what the choice MEANS and the rest
   of the plan's CRUD. It is BUILT at every detent — opening the sheet has nothing to fetch
   and nothing to compute — but `display:none` genuinely removes it from the accessibility
   tree, from tab order and from browser Find until the sheet is at `full`. That is the
   intended behaviour (it is the sheet's third page, not hidden content), and it is stated
   here because an earlier note claimed the opposite. */
.ce-fullonly{display:none}
.ce-sheet[data-detent="full"] .ce-fullonly{display:block}
/* KIERTOSUUNTA: the app's own .seg, restyled — same control as the Porras form, so two
   surfaces do not teach two gestures. NEITHER button is on until a human chooses one:
   an unchosen hand is a state, not a default. */
.ce-hand{margin-top:2px;display:flex;gap:6px}
.ce-hand button{flex:1;min-height:44px;padding:11px 12px;border:1px solid var(--cad-edge-2);border-radius:8px;
  background:var(--cad-field);color:var(--cad-ink);font-family:var(--cad-mono);font-size:12.5px;cursor:pointer}
.ce-hand button.on{background:var(--cad-ann);border-color:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
/* A FULL-WIDTH ACTION IN THE SHEET, and the selector has to carry `.btn` to be one: the
   kit's `.btn.sm{width:auto;max-width:none}` is two classes and was quietly beating a
   one-class `.ce-full`, so "Palauta suorakulmioksi", "Poista kärki" and "Poista" have been
   shrink-wrapped buttons floating at the left margin since the shell landed. `min-height`
   is here for the same reason — .btn.sm's padding lands at ~40 px, under the 44 px every
   other control on this screen keeps. */
.ce-full,.btn.ce-full{width:100%;max-width:none;min-height:44px;justify-content:center;margin-top:8px}
.ce-full.danger,.btn.ce-full.danger{color:var(--cad-bad);border-color:color-mix(in srgb,var(--cad-bad) 45%,transparent)}
/* MUUTOSKOOSTE plate: the mould counts first as a grid, then the server's own rows. */
.ce-kgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:12px 0}
.ce-kcell{background:var(--cad-field);border-radius:8px;padding:9px 10px;display:flex;flex-direction:column;gap:2px}
.ce-kcell.ok{background:rgba(79,169,126,.14)}
.ce-kcell.warn{background:rgba(240,177,90,.14)}
.ce-kn{font-family:var(--cad-mono);font-size:20px;font-variant-numeric:tabular-nums;color:var(--cad-geo)}
.ce-kcell.ok .ce-kn{color:var(--cad-ok)}
.ce-kcell.warn .ce-kn{color:var(--cad-warn)}
.ce-kl{font-family:var(--cad-mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--cad-quiet);line-height:1.3}
/* NO SCROLL BOX OF ITS OWN. `max-height + overflow:auto` turned the summary into a little
   scrolling box inside a scrolling sheet: 9 of 45 rows visible, and what fell off the
   bottom every time was the MÄÄRÄT block and §7's only sentence. The sheet scrolls. */
.ce-krivit{border-top:1px solid var(--cad-edge);padding-top:10px}
.ce-krivi{font-family:var(--cad-mono);font-size:12px;line-height:1.55;white-space:pre-wrap;
  font-variant-numeric:tabular-nums;color:var(--cad-ink)}
.ce-krivi.sisennys{color:var(--cad-quiet)}
.ce-khuom{margin-top:10px;font-size:11.5px;color:var(--cad-quiet);line-height:1.45}
@media(max-width:899px){.ce-kgrid{grid-template-columns:repeat(2,1fr)}}

/* the object palette: rows, in the CAD's own ink */
.ce-pallist{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:6px}
.ce-pal{display:flex;flex-direction:column;align-items:flex-start;gap:2px;border:1px solid var(--cad-edge-2);
  border-radius:8px;background:var(--cad-field);padding:11px 12px;cursor:pointer;text-align:left;font:inherit;
  color:var(--cad-geo);min-height:56px}
.ce-pal:hover{border-color:var(--cad-ann)}
.ce-pal.wide{grid-column:1/-1;margin-top:8px}
.ce-pal-n{font-family:var(--cad-mono);font-size:13px}
.ce-pal-s{font-size:11.5px;color:var(--cad-quiet)}
/* the two sheets these screens own carry the CAD's own heading, not the kit's display face */
#cePalSheet h3,#ceKoosteSheet h3{font-family:var(--cad-mono);font-size:14px;font-weight:500;
  letter-spacing:.04em;color:var(--cad-geo)}
#cePalSheet .sub,#ceKoosteSheet .sub{color:var(--cad-quiet)}

/* ── 8. desktop: the sheet becomes a column, the rail gets denser ─────────────────────
   A pointer has no thumb, and a 1280 px window has height to spare on the right rather
   than at the bottom — so the sheet un-docks into a side column and the detents stop
   applying (there is nothing to uncover). Everything else is identical. */
@media(min-width:900px){
  /* the sheet is a column BESIDE the drawing here, so it covers none of it: the rail is
     centred in the whole canvas again, and it is the same rule that says so. cad.js keeps
     the inline value off this element in exactly the state `ceSheetGeom` returns null for. */
  #cadedit{--ce-sheet-h:0px}
  .ce-sheet{position:static;height:auto;max-height:none;flex:0 0 300px;border-top:0;
    border-left:1px solid var(--cad-edge);box-shadow:none;padding:0 14px 14px;overflow-y:auto;transition:none}
  .ce-sheet[data-detent="half"],.ce-sheet[data-detent="full"]{height:auto;box-shadow:none}
  .ce-grab{display:none}
  .ce-peek{cursor:default;padding-top:10px}
  .ce-peek-c{display:none}
  .ce-pbody{display:block;overflow:visible;padding-top:10px}
  .ce-sheet .ce-pbody > .ce-p-title{display:block}
  /* THE DETENTS DO NOT APPLY HERE — the sheet is a static column with the whole window's
     height, so there is no "one detent further down" to hide the model's numbers behind. */
  .ce-fullonly{display:block}
  /* 300 px cannot hold two 44 px actions side by side without clipping their labels */
  .ce-eh-acts{flex-direction:column}
  .ce-lgrid{grid-template-columns:1fr 1fr}
  .ce-body:has(.ce-sheet[data-detent="half"]) .ce-scalebar,
  .ce-body:has(.ce-sheet[data-detent="full"]) .ce-scalebar,
  .ce-body:has(.ce-sheet[data-detent="half"]) .ce-banners,
  .ce-body:has(.ce-sheet[data-detent="full"]) .ce-banners{opacity:1}
  .ce-scalebar{bottom:14px}
  .ce-banners{bottom:40px}
  .ce-p-sub,.ce-p-note{font-size:11px}
}

/* ── 8b. …and DENSITY follows the POINTER, not the width ─────────────────────────────
   The layout above is a question about WIDTH (does a 300 px column fit beside the
   drawing), and 900 px answers it. Shrinking the targets is a different question — does
   this person have a mouse — and 900 px answers it WRONGLY: an iPad in landscape is
   1024 × 768, a plausible site device, and it was getting 36 px rail buttons and 34 px
   steppers with no drag sheet to fall back on. `pointer:fine` is the actual condition,
   so a touch tablet keeps the phone's 44 px floor inside the desktop layout. */
@media(min-width:900px) and (pointer:fine){
  /* the min-* floors above are the TOUCH floor; a mouse pointer is allowed the denser box,
     so they are relaxed here too — leaving them at 44 would make `width:36px` a dead letter */
  .ce-fbtn{width:36px;height:36px;min-width:36px;min-height:36px}
  .ce-fbtn .ic{width:17px;height:17px}
  .ce-seg-b{min-width:38px;min-height:36px;font-size:12px}
  .ce-step button{min-height:38px;width:34px}
  .ce-step .inp{min-height:38px;font-size:14px}
  .ce-hand button{min-height:38px}
  .ce-stchip{min-height:36px}
}

/* ── 9. reduced motion ───────────────────────────────────────────────────────────────
   The sheet still moves between detents — it just arrives instead of gliding. */
@media (prefers-reduced-motion:reduce){
  .ce-sheet,.ce-peek-c,.ce-grab::before,.ce-fbtn,.ce-seg-b,.ce-step button,.ce-rail{transition:none}
}

/* ══════════════════════════════════════════════════════════════════════════════════════
   LUONNOS — "Piirrä pohja" (public/js/sketch.js)

   Same shell as the editor, because it IS the same job at a different speed: a stroke says
   WHAT is WHERE, the editor says how many millimetres. It keeps its own bottom action bar
   (Tulkitse · Kuvaa paperi · Valmis are three distinct commits, not tools) and its own
   floating mode switch.
   ══════════════════════════════════════════════════════════════════════════════════════ */
.sk-canvas{cursor:crosshair}
.sk-hud{flex-direction:row;flex-wrap:wrap;gap:5px;max-width:calc(100% - 160px)}
/* piirtotila kankaan alalaidassa, keskellä: peukalon ulottuvilla, ei omalla rivillään */
.ce-float.modes{left:50%;bottom:12px;transform:translateX(-50%);gap:0;background:rgba(20,23,27,.86);
  border:1px solid var(--cad-edge-2);border-radius:10px;overflow:hidden;max-width:calc(100% - 24px);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.sk-mode{min-height:44px;padding:0 14px;border:0;background:transparent;color:var(--cad-ink);
  font-family:var(--cad-mono);font-size:12.5px;cursor:pointer;white-space:nowrap}
.sk-mode + .sk-mode{border-left:1px solid var(--cad-edge)}
.sk-mode.on{background:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
/* luonnoksen omat huomiot nousevat tilavalitsimen yli */
#sketch .ce-banners{bottom:calc(78px + var(--safe-bot));right:66px}
.sk-chip{background:rgba(201,205,212,.12);color:var(--cad-ink);font-family:var(--cad-mono);font-size:11px;
  border-radius:3px;padding:4px 9px}
.sk-chip.unknown{background:rgba(240,177,90,.16);color:var(--cad-warn)}
.sk-note{border-radius:4px;padding:8px 11px;font-size:12px;font-weight:600;line-height:1.4;
  background:rgba(21,24,28,.92);border:1px solid var(--cad-edge);color:var(--cad-ink)}
.sk-note.warn{color:var(--cad-warn);border-color:color-mix(in srgb,var(--cad-warn) 45%,transparent)}

/* muste: elävä veto on merkintäväriä, tunnistamaton jää näkyviin "?"-merkillä */
.sk-ink{fill:none;stroke:var(--cad-ann);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;opacity:.9;vector-effect:non-scaling-stroke}
.sk-ink.live{opacity:1}
.sk-ink.unknown{stroke:var(--cad-warn);stroke-dasharray:7 5}
.sk-ink.on{stroke-width:4.5}
/* MORPH: tunnistettu veto sulaa objektiksi ~150 ms — käyttäjä NÄKEE mitä tapahtui */
.sk-ink.out{animation:sk-fade-out .15s ease-out forwards}
.sk-morph{animation:sk-pop-in .15s ease-out}
@keyframes sk-fade-out{from{opacity:.9}to{opacity:0}}
@keyframes sk-pop-in{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .sk-ink.out{animation:none;opacity:0}
  .sk-morph{animation:none}
}

.sk-wall{fill:var(--cad-geo);opacity:.1;stroke:none}
.sk-room{fill:none;stroke:currentColor;stroke-width:2.4;vector-effect:non-scaling-stroke}
.sk-dim{fill:var(--cad-dim);font-family:var(--cad-mono);text-anchor:middle}
.sk-obj rect{fill:none;stroke:currentColor;stroke-width:2;vector-effect:non-scaling-stroke}
.sk-obj text{fill:currentColor;opacity:.75;font-family:var(--cad-mono)}
.sk-obj line{stroke:currentColor;stroke-width:2;vector-effect:non-scaling-stroke}
.sk-obj .sk-x{stroke-width:1;opacity:.45}
.sk-obj.punch rect{stroke-width:2.6}
.sk-obj.on rect{stroke:var(--cad-ann)}
/* siemennetty (ei piirretty) pylväs on vahvistamaton: katkoviiva, kuten odottava arvo */
.sk-obj.seeded rect{stroke-dasharray:8 5;opacity:.75}
.sk-obj .sk-ovi-gap{fill:var(--cad-canvas);stroke:currentColor;stroke-width:1.6;vector-effect:non-scaling-stroke}
.sk-obj .sk-north-ring{fill:none;stroke:currentColor;stroke-width:1.2;opacity:.5;vector-effect:non-scaling-stroke}
.sk-obj .sk-north-head{fill:currentColor;stroke:none}
.sk-obj .sk-fanarc{fill:none;stroke:var(--cad-ann);stroke-width:2.4;vector-effect:non-scaling-stroke}
.sk-badge circle{fill:var(--cad-warn);stroke:var(--cad-canvas);stroke-width:2;vector-effect:non-scaling-stroke}
.sk-badge text{fill:var(--cad-canvas);font-family:var(--cad-mono);font-weight:700;text-anchor:middle}
/* Mitä pohjassa JO on: haaleat aaveet ikkunan alla. Ne eivät ole vetoja — niitä ei voi
   valita eikä poistaa täällä (editori omistaa ne) — mutta ilman niitä luonnos näyttää
   tyhjältä täydelläkin pohjalla, ja piirretty aukko olisi hiljaa toinen samanlainen. */
.sk-exist rect{fill:none;stroke:currentColor;stroke-width:1.6;stroke-dasharray:5 6;opacity:.4;vector-effect:non-scaling-stroke}
.sk-exist text{fill:currentColor;opacity:.4;font-family:var(--cad-mono)}
.sk-exist-gap{fill:var(--cad-canvas);stroke:var(--cad-dim);stroke-width:1.2;stroke-dasharray:5 6;opacity:.45;vector-effect:non-scaling-stroke}

/* aloitusvalinta + tyypin valinta: samat rivit kuin objektipaletissa */
.sk-seed,.sk-pick{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;
  border:1px solid var(--line-strong);border-radius:var(--rb);background:var(--input-bg);padding:12px 13px;
  margin-top:8px;cursor:pointer;text-align:left;font:inherit;color:var(--text);min-height:56px}
.sk-seed:hover,.sk-pick:hover{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 8%,transparent)}
.sk-picks{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:6px}
.sk-picks .sk-pick{margin-top:0}
.sk-pick.on{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 12%,transparent)}
.sk-pick.wide{grid-column:1/-1}
.sk-pick.danger{border-color:color-mix(in srgb,var(--danger) 40%,var(--line-strong));color:var(--danger)}
.sk-seed-n,.sk-pick-n{font-size:13.5px;font-weight:800}
.sk-seed-s,.sk-pick-s{font-size:11.5px;color:var(--text-2);line-height:1.35}
@media(max-width:600px){.sk-picks{grid-template-columns:1fr}}

/* alapalkki — VAIN luonnoksella: Tulkitse · Kuvaa paperi · Valmis. Pohjaeditorilla ei ole
   alapalkkia lainkaan: Lisää ja Piirrä ovat työkalupalkissa ja Valmis on otsikkorivin
   paluunappi, joten se rivi on annettu piirustukselle. */
.ce-bar{flex:0 0 auto;background:var(--cad-panel);border-top:1px solid var(--cad-edge);
  padding:8px 14px calc(var(--safe-bot) + 8px)}
.ce-bar .inner{display:flex;gap:9px;max-width:760px;margin:0 auto}
.ce-bar .ebtn{flex:1;min-height:44px;padding:10px 12px;border-radius:8px;font-family:var(--cad-mono);
  font-size:12.5px;letter-spacing:.02em}
.ce-bar .ebtn.add{background:rgba(247,148,29,.14);color:var(--cad-ann);border:1px solid rgba(247,148,29,.4)}
.ce-bar .ebtn.prop,.ce-bar .ebtn.sketch{background:var(--cad-field);color:var(--cad-ink);border:1px solid var(--cad-edge-2)}
.ce-bar .ebtn.done{background:var(--cad-ann);color:var(--cad-canvas);font-weight:600}
@media(min-width:900px){
  .ce-bar .inner{max-width:none;justify-content:flex-end}
  .ce-bar .ebtn{flex:0 0 auto;min-width:150px}
}
/* the mode switch is a TARGET, so it shrinks on the same condition the rail does */
@media(min-width:900px) and (pointer:fine){
  .sk-mode{min-height:36px;font-size:12px;padding:0 12px}
}

/* ── 10. KOKO RAPPU — kantajat (porras / hissi) objektilistassa ───────────────────────
   Ne eivät kuulu yhdelle pohjatyypille, joten ne erottuvat omana ryhmänään: sama rivi
   toistuu joka kortissa, ja sen pitää näyttää siltä että se ON sama.

   MITÄ 390 PIKSELIÄ PALJASTI. Ensimmäinen versio tarkastettiin vain työpöytäleveydellä.
   Puhelimessa siitä näkyi neljä eri vikaa, ja kaikki neljä ovat tässä lohkossa:

   (a) Paletti ei ollut voimassa. Kaikki tämän ryhmän var(--cad-*) -viittaukset osuivat
       #editor-ruudulle, jossa niitä nimiä ei ollut — reunat, avatun rivin täyttö ja
       varoituksen väri katosivat äänettömästi. Korjattu osassa 1 (.rappu-block on nyt
       paletin nimissä). Siksi TÄSSÄ ei ole yhtään uutta tokenia: nimet olivat oikein,
       vain niiden ulottuvuus oli väärä.

   (b) 15 kulman ruudukko oli repeat(auto-fill, minmax(72px, 1fr)) ilman solun omaa
       taustaa: 390:llä siitä tuli neljä saraketta numeroita ilman rajoja — numeromuuri,
       josta ei erota mikä luku kuuluu mille elementille. Nyt solu on 116 px leveä ja sillä
       on oma pinta ja reuna, joten puhelimeen tulee KAKSI saraketta ja työpöydälle viisi:
       sama taulukko, luettava molemmissa. Numerot ovat monospacea, koska tässä ruudussa
       melkein jokainen merkkijono ON dataa — sama sääntö kuin muualla tässä tiedostossa.

   (c) Kosketuskohteet. Kantajan rivi oli n. 44 px vain siksi että tekstiä sattui olemaan
       kaksi riviltä; se on onnenkauppaa, ei mitoitus. Nyt rivillä on min-height 52 px.

   (d) Ryhmä näytti samalta kuin kerroksen omat objektit. Nyt se on oma paneelinsa: CAD:n
       kangas, oranssi reunaviiva ja mono-otsikko. Se on rapun tason tietoa keskellä
       kerroksen lomaketta, ja sen eron pitää näkyä ennen kuin tekstiä ehtii lukea. */
.rappu-block {
  margin: 2px 0 12px; padding: 11px 11px 12px;
  background: var(--cad-canvas);
  border: 1px solid var(--cad-edge); border-left: 3px solid var(--cad-ann);
  border-radius: 4px;
}
.rappu-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; margin-bottom: 6px; }
.rappu-tag { font-family: var(--cad-mono); font-size: 10.5px; font-weight: 600; letter-spacing: .16em;
  text-transform: uppercase; color: var(--cad-ann); }
.rappu-sub { font-family: var(--cad-mono); font-size: 10.5px; letter-spacing: .06em; color: var(--cad-quiet); }
/* Selite ENNEN rivejä, leipäkokoisena: se kertoo miksi ryhmä on olemassa, eikä alaviite
   kerro sitä kenellekään joka selaa listaa peukalolla. */
.rappu-why { margin: 0 0 10px; font-size: 12.5px; line-height: 1.4; color: var(--cad-dim); max-width: 62ch; }
.rappu-objs { display: flex; flex-direction: column; gap: 8px; }
.carrier { display: flex; flex-direction: column; }

.carrierrow { padding: 0; background: var(--cad-panel); border-color: var(--cad-edge-2); border-radius: 3px; }
.carrierrow .obj-edit { flex: 1; flex-direction: row; align-items: center; justify-content: space-between;
  gap: 10px; width: 100%; min-height: 52px; padding: 9px 12px; }
.carrier-txt { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; flex: 1; min-width: 0; }
.carrierrow .obj-name { font-size: 13.5px; font-weight: 700; color: var(--cad-geo); }
.carrierrow .obj-meta { font-size: 11.5px; line-height: 1.35; color: var(--cad-dim); overflow-wrap: anywhere; }
/* Avattu rivi ja sen taulukko ovat YKSI ääriviivattu kappale annotaatiovärillä — tässä
   tiedostossa oranssi tarkoittaa valintaa, ja avattu kantaja on valittu kohde. */
.carrierrow.open { border-color: var(--cad-ann); border-radius: 3px 3px 0 0; }
.carrierrow.open .obj-edit { background: var(--cad-field); }
/* Avautumaton kantaja perii .obj-edit:n osoitinkursorin — nollataan, ettei se lupaa
   hiirelläkään napautusta jota ei ole. */
.objrow .obj-edit.carrier-static { cursor: default; }
.carrier-btn:hover { background: var(--cad-field); }
.carrier-btn:focus-visible { outline: 2px solid var(--cad-ann); outline-offset: -2px; }
/* Kulmamerkki = "tämä rivi aukeaa". Piirretty reunoista, ei kirjasimen merkillä: sama
   kulma jokaisella laitteella, eikä se ole riippuvainen fontin kattavuudesta. */
.carrier-caret { flex: 0 0 auto; width: 9px; height: 9px; margin-right: 3px;
  border-right: 2px solid var(--cad-ann); border-bottom: 2px solid var(--cad-ann);
  transform: rotate(-45deg); transition: transform .15s ease; }
.carrierrow.open .carrier-caret { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .carrier-caret { transition: none; } }

.carrierbody { padding: 0; }
.elpanel { padding: 10px 11px 11px; background: var(--cad-field);
  border: 1px solid var(--cad-ann); border-top: 0; border-radius: 0 0 3px 3px; }
/* Otsikko ja kappalemäärä VIERETYSTEN, ei space-betweenillä: leveällä ruudulla ne
   ajautuivat rivin vastakkaisiin päihin, jolloin ne lakkasivat olemasta sama otsikko. */
.elhead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 14px; margin-bottom: 3px; }
.elhead-t { font-family: var(--cad-mono); font-size: 10px; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--cad-quiet); }
.elhead-n { font-family: var(--cad-mono); font-size: 11.5px; font-weight: 600; color: var(--cad-ann);
  font-variant-numeric: tabular-nums; }
.elmodel { font-size: 12.5px; font-weight: 700; line-height: 1.35; color: var(--cad-geo);
  margin-bottom: 9px; overflow-wrap: anywhere; }
.elwarn { font-size: 12px; font-weight: 600; line-height: 1.4; color: var(--cad-warn);
  background: color-mix(in srgb, var(--cad-warn) 14%, transparent);
  border-left: 3px solid var(--cad-warn); border-radius: 2px; padding: 7px 9px; margin-bottom: 9px; }
/* Mitoitettu leveys, ei koko ruudun leveys: rajaamattomana 15 elementtiä levisi
   työpöydällä yhdeksään sarakkeeseen ja muuttui nauhaksi, jolta yhden elementin
   etsiminen on silmän matka. 760 px pitää sen taulukkona kaikilla leveyksillä. */
.ellist { list-style: none; margin: 0 0 9px; padding: 0; max-width: 760px;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); gap: 6px; }
/* auto + 1fr, ei space-between: numerot vasemmalle omaan sarakkeeseensa ja asteet oikeaan
   reunaan. EIVÄT desimaalipilkut osu allekkain, ja se väite oli tässä väärin: fmtDeg
   katkaisee lopun nollat ja °-merkki on samassa spanissa, joten sarakkeen oikea reuna on
   aste-merkki eikä pilkku — "12°" ja "12,525°" ovat samassa taulukossa. Sarake on suora,
   pilkkusarake ei ole; luettavuus tulee tabular-nums:sta joka pitää numeroiden leveydet
   vakiona. */
.elrow { display: grid; grid-template-columns: auto 1fr; align-items: center; column-gap: 8px;
  min-height: 34px; padding: 6px 9px; background: var(--cad-panel);
  border: 1px solid var(--cad-edge-2); border-radius: 2px; }
.elno { font-family: var(--cad-mono); font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  color: var(--cad-quiet); font-variant-numeric: tabular-nums; }
.elang { font-family: var(--cad-mono); font-size: 13px; font-weight: 600; text-align: right;
  color: var(--cad-geo); font-variant-numeric: tabular-nums; }
.elnote { margin: 0; font-size: 12px; line-height: 1.4; color: var(--cad-dim); max-width: 62ch; }

/* ── Piirustuksen selite kuvan ALLA ──────────────────────────────────────────
   Selite on §7:ää eikä sitä lyhennetä; se vain ei enää kilpaile geometriasta
   samasta laatikosta. Avoinna työpöydällä, suljettuna puhelimessa. */
.cad-selite { margin: 8px 0 2px; border-top: 1px solid var(--cad-edge); padding-top: 8px; }
.cad-selite > summary { font: 800 11px/1.4 Archivo, system-ui, sans-serif; letter-spacing: .04em;
  text-transform: uppercase; color: var(--cad-quiet); cursor: pointer; padding: 4px 0; min-height: 32px;
  display: flex; align-items: center; }
.cad-selite > summary:hover { color: var(--cad-ink); }
.cad-selite p { margin: 0 0 7px; font: 400 12px/1.55 Barlow, system-ui, sans-serif; color: var(--cad-ink);
  max-width: 68ch; }
.cad-selite p:last-child { margin-bottom: 2px; }

/* ── Objektin väri: nimetty joukko, ei valitsin ──────────────────────────────
   Sävyt erottuvat myös VAALEUDELTAAN, koska pöytäkirja tulostetaan eikä
   harmaasävytulostin näytä sävyeroa. "—" on tyypin oma väri, ei harmaa. */
.objcolors { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 4px; }
/* KIT-TOKENIT, EI --cad-*: tämä lomake on projektinäkymässä, jossa CAD-paletti ei ole
   voimassa (ks. :root-lohko yllä). Reuna ja tausta olivat sen takia näkymättömät. */
.objcolor { width: 40px; height: 40px; min-width: 40px; border-radius: 3px; cursor: pointer;
  border: 2px solid var(--line-strong); background: var(--input-bg); color: var(--text-2);
  font: 700 13px/1 Barlow, system-ui, sans-serif; }
.objcolor.on { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary) inset; }

/* ── Vaihevalitsin lomakkeessa ja merkintä listassa ──────────────────────────
   Markus: "there is current aukko and aukko that we cut."
   Sanoin, ei sävyin: "nykyinen" ja "uusi" luetaan myös harmaasävynä. "—" on yhtä
   lailla valinta — merkitsemätön objekti ei väitä olevansa kumpikaan. */
.objstages { display: flex; gap: 6px; flex-wrap: wrap; }
.objstage { min-height: 40px; padding: 0 16px; border-radius: 3px; cursor: pointer;
  border: 2px solid var(--line-strong); background: var(--input-bg); color: var(--text-2);
  font: 700 13px/1 Barlow, system-ui, sans-serif; }
/* TÄYTETTY, ei pelkkä reunasävy: valinta on tämän kentän koko sisältö, ja pelkkä
   reunaväri katoaa 390 px:n ruudulla kolmen samannäköisen napin joukkoon. Mitattu:
   ensimmäinen versio näytti kaikki kolme identtisinä puhelimessa. */
.objstage.on { background: var(--primary); border-color: var(--primary); color: var(--surface); }
.obj-vaihe { font: 700 10px/1 Barlow, system-ui, sans-serif; text-transform: uppercase;
  letter-spacing: .04em; padding: 2px 5px; border-radius: 2px; margin-left: 6px;
  border: 1px solid var(--line-strong); color: var(--text-2); }
.obj-vaihe.v-uusi { border-color: var(--primary); color: var(--primary); }

/* ── Vaihenäkymä: kumpaa taloa katsot (#59) ──────────────────────────────────
   Oma rivinsä pohjatyyppien YLÄPUOLELLA eikä kortin sisällä: valinta koskee koko
   projektia, ja kortin sisällä se näyttäisi kerroskohtaiselta. Kortin näköinen
   laatikko, jotta rivi ei jää leijumaan listan päälle irrallisena. */
.vaiherivi { border: 1px solid var(--line); border-radius: 3px; background: var(--surface);
  padding: 12px 14px; margin-bottom: 12px; }
.vaihenakymat { display: flex; gap: 6px; flex-wrap: wrap; margin: 2px 0 6px; }
/* SAMA MITTA JA SAMA TÄYTTÖ kuin .objstage: molemmat ovat "kumpi vaihe" -valintoja, ja
   kaksi eri näköistä valitsinta samasta käsitteestä lukisi kahtena eri asiana. */
.vaihenakyma { min-height: 40px; padding: 0 16px; border-radius: 3px; cursor: pointer;
  border: 2px solid var(--line-strong); background: var(--input-bg); color: var(--text-2);
  font: 700 13px/1 Barlow, system-ui, sans-serif; }
.vaihenakyma.on { background: var(--primary); border-color: var(--primary); color: var(--surface); }
/* Piirroksen oma näkymäotsikko (SVG:n .vaiheen-nakyma) — sama painotus kuin viuhkan
   lähdelohkolla, koska molemmat vastaavat kysymykseen "mikä tämä kuva on". */
.piirustus .vaiheen-nakyma text { fill: currentColor; }

/* ── Portaan asento: kierto + pylvään paikka ─────────────────────────────────
   Kolme kertaa kolme nappia = aukko pienoiskoossa, joten paikka VALITAAN kuvasta
   eikä kahdesta millimetriluvusta. Ruudukko on neliö ja sen suhde on aukon
   suhde vain sattumalta — se on valitsin, ei mittakuva, ja siksi napit ovat
   yhtä suuria. Kosketuskoko 34 px + 6 px väli = 40 px kohde. */
.asento { padding: 12px 14px; border-bottom: 1px solid var(--line); }
.asento-lbl, .asento-lbl2 { font: 800 11px/1 Archivo, system-ui, sans-serif;
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-2); margin-bottom: 6px; }
.asento-lbl2 { margin-top: 12px; }
.asento-why, .asento-note { font: 400 12px/1.45 Barlow, system-ui, sans-serif;
  color: var(--text-2); margin: 0 0 10px; }
.asento-note { margin: 8px 0 0; }
.asento-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.asento-k { font: 700 13px/1 Barlow, system-ui, sans-serif; color: var(--text); }
.asento-v { flex: 1 1 120px; font: 400 12px/1.35 Barlow, system-ui, sans-serif; color: var(--text-2); }
.paikkagrid { display: grid; grid-template-columns: repeat(3, 34px); gap: 6px; }
.paikkanappi { width: 34px; height: 34px; border-radius: 2px; cursor: pointer; position: relative;
  border: 2px solid var(--line-strong); background: var(--input-bg); }
.paikkanappi.on { border-color: var(--primary); }
/* Piste kertoo MISSÄ kohtaa aukkoa napa on. Ilman sitä nämä ovat yhdeksän identtistä tyhjää
   ruutua: title ei näy kosketuksessa ja aria-label vain ruudunlukijalle. */
.paikkapiste { position: absolute; width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-2); transform: translate(-50%, -50%); pointer-events: none; }
.paikkanappi.on .paikkapiste { background: var(--primary); width: 12px; height: 12px; }
.objcolor.c-sininen { background: var(--cad-obj-sininen); }
.objcolor.c-vihrea { background: var(--cad-obj-vihrea); }
.objcolor.c-keltainen { background: var(--cad-obj-keltainen); }
.objcolor.c-punainen { background: var(--cad-obj-punainen); }
.objcolor.c-harmaa { background: var(--cad-obj-harmaa); }

/* ── Objektin oma väri PIIRROKSESSA ──────────────────────────────────────────
   Ryhmät piirtyvät currentColorilla, joten värin asettaminen `color`-arvona
   värjää sekä viivan että tekstin, ja se periytyy myös tulosteeseen — toisin
   kuin fill/stroke, jotka pitäisi asettaa joka elementille erikseen.
   print-color-adjust pitää sävyn myös tulostimella joka muuten pudottaa
   taustavärit (sama syy kuin pöytäkirjan EHDOTUS-leimassa). */
.obj-c-sininen { color: var(--cad-obj-sininen); }
.obj-c-vihrea { color: var(--cad-obj-vihrea); }
.obj-c-keltainen { color: var(--cad-obj-keltainen); }
.obj-c-punainen { color: var(--cad-obj-punainen); }
.obj-c-harmaa { color: var(--cad-obj-harmaa); }
@media print {
  .obj-c-sininen, .obj-c-vihrea, .obj-c-keltainen, .obj-c-punainen, .obj-c-harmaa {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }
}

/* ── Vaihe piirroksessa: nykyinen vs uusi ────────────────────────────────────
   VIIVAN PAINO, EI VÄRI. Väri on käyttäjän oma (objektin väri yllä), joten vaihe
   ei voi lainata sitä; harmaasävytuloste ja valokopio taas syövät sävyn mutta eivät
   viivan paksuutta. Nykyinen piirtyy himmeämpänä — se on lähtötilanne, tausta jota
   vasten muutos luetaan. Piirroksessa lukee lisäksi "(nykyinen)" / "(uusi)" nimen
   perässä: TEKSTI on ensisijainen kantaja, tämä on korostus. Merkitsemätön objekti
   ei saa kumpaakaan luokkaa eikä siis muutu lainkaan — vanha piirros pysyy
   tavulleen samana (todistettu vertaamalla edelliseen piirtäjään).

   VIIVANPAKSUUS EI OLE TÄÄLLÄ vaan piirtäjässä (pohjakuva.ts vaiheenViivapaino):
   piirroksen koordinaatisto on MILLIMETREJÄ, joten CSS:n `stroke-width: 1` olisi
   yksi millimetri — näkymätön kolmen metrin kuvassa. Vain mittakaavaton
   ominaisuus kuuluu tänne. */
.vaihe-nykyinen { opacity: .62; }

/* ── Kiertosuunnan nuoli kankaalla ───────────────────────────────────────────
   Merkintä, ei geometriaa: erottuu annotaatiovärillä kuten valinta ja mitat,
   eikä osallistu osumatestiin (pointer-events: none). */
.ce-fan-dir { pointer-events: none; }
.ce-fan-dir-l, .ce-fan-dir-h { stroke: var(--cad-ann); stroke-width: 2; fill: none;
  stroke-linecap: round; opacity: .9; }

/* ── Kääntö portaan paneelissa (CAD-editori) ─────────────────────────────────
   Nappi + luettava arvo samalla rivillä: "Käännä 90°" on TEKO, sen vieressä lukee
   mihin asentoon se jäi. Valitsematta on oma sanansa eikä 0° — sovellus ei väitä
   valintaa jota kukaan ei ole tehnyt (#53). */
.ce-kaanto { display: flex; align-items: center; gap: 10px; }
.ce-kaanto-btn { min-height: 40px; padding: 0 16px; border-radius: 3px; cursor: pointer;
  border: 1px solid var(--cad-edge-2); background: var(--cad-field);
  color: var(--cad-ink); font: 700 13px/1 Barlow, system-ui, sans-serif; }
.ce-kaanto-btn:active { transform: scale(.97); }
.ce-kaanto-arvo { font: 700 13px/1 var(--cad-mono); color: var(--cad-quiet); }

/* ── Portaan spec-kentät paneelissa (siirretty projektinäkymästä 2026-08-06) ──
   Samat kosketuskoot kuin muissa CAD-säätimissä. 16 px fonttikoko on iOS-vaatimus:
   sitä pienempi laukaisee automaattisen zoomin kun kenttään koskee. */
.ce-inp { width: 100%; min-height: 40px; padding: 0 10px; border-radius: 3px;
  border: 1px solid var(--cad-edge-2); background: var(--cad-field); color: var(--cad-ink);
  font: 500 16px/1 var(--cad-mono); }
.ce-inp:disabled { opacity: .55; }
.seg.ce-theta { display: flex; gap: 6px; }
.seg.ce-theta button { flex: 1; min-height: 40px; border-radius: 3px; cursor: pointer;
  border: 1px solid var(--cad-edge-2); background: var(--cad-field); color: var(--cad-ink);
  font: 700 13px/1 Barlow, system-ui, sans-serif; }
.seg.ce-theta button.on { background: var(--cad-ann); border-color: var(--cad-ann); color: var(--cad-canvas); }
