/* Asterism — the shared canvas.
 *
 * Thesis (place/design/cozy-antigravity/THESIS.md):
 *   chrome is not an instrument. It is a warm desk of wood and paper beneath a
 *   nighttime skylight, on which the sky is drawn close to the hands.
 *
 * The sky is the skylight and is never touched by this file: stars, deep-sky
 * objects and the ESO plates keep their own rendering. What changed is the rim.
 * Every chrome surface is opaque roasted walnut at luminance ~0.02, which is
 * below the faintest star this canvas draws, so the desk can be warm without
 * ever becoming the brightest thing on screen. Opaque, not translucent: the
 * contrast of every label is then the same over black sky, over a dense star
 * field and over M42, and can be measured once instead of hoped for.
 *
 * Radii come from the objects on that desk rather than from a taste for round
 * corners: 8px is a wooden peg, 14px is a tool held in the hand, 20px is a
 * sheet of heavy paper, and 50% is reserved for a star.
 */
:root {
  /* True sky behind the skylight. Kept as the reference black. */
  --black: #030405;
  /* The desk rail: dark roasted walnut, luminance 0.018, below every star. */
  --surface-desk: #1b1613;
  /* Raised woodwork -- pegs, buttons, inlays -- one tone above the rail. */
  --surface-raised: #271f1a;
  /* Record cards and folios: dark parchment, still safe for night adaptation. */
  --surface-card: #29211b;
  /* Carved grooves: meter tracks and input wells read as slots cut in wood. */
  --surface-recessed: #120e0c;

  /* Warm parchment ink, 12.8:1 on walnut, without the glare of pure white. */
  --ink: #f5ede0;
  /* Secondary lettering, 7.6:1. */
  --muted: #baa996;
  /* The faintest enabled mark: 5.32:1 on walnut, 4.69:1 on card. Both clear AA.
     An earlier value (#8c7b6b) failed on card and was measured out. */
  --dim: #9a8978;
  /* Lantern amber: primary actions and current values only. */
  --amber: #f5b642;
  /* The reading edge; gives dark type inside the Draw button 11.2:1. */
  --amber-high: #ffd276;
  /* Terracotta is a red-pencil correction mark, not a generic danger tint. */
  --correction: #e07a5f;
  /* Sage marks a completed observation and stays clear of the green swatch. */
  --success: #81b29a;
  /* Joinery line: the weakest boundary that still measures 3.8:1 on walnut. */
  --line-wood: #4a3a30;
  /* The strong rule, for boundaries between groups rather than inside one. */
  --line-strong: #7d6555;
  /* Candlelight ivory, visible against both walnut and amber. */
  --focus: #ffe89e;

  /* The five drawing colours are already distributed and never change
     (CLAUDE.md §5). Byte for byte as they shipped. */
  --swatch-gold: #f4c76f;
  --swatch-white: #ffffff;
  --swatch-cyan: #7fd4ff;
  --swatch-pink: #ff8ab5;
  --swatch-green: #8ef0a8;

  /* Radii, from the things on the desk. */
  --radius-peg: 8px;    /* a small wooden button pressed with the thumb */
  --radius-hand: 14px;  /* a tool held in the hand: Draw, inputs, toasts */
  --radius-sheet: 20px; /* heavy paper trimmed by hand: cards and dialogs */
  --radius-star: 50%;   /* a star disc. Colour swatches only. */

  --line-fine: 1px;
  --line-focus: 2px;
  --focus-offset: 3px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;

  /* Apple HIG minimum. The previous 34px controls here were below it; the
     shared sky is a touch surface first, so this is now enforced rather than
     aspired to, and the layout is built around 44 instead of around 34. */
  --tap: 44px;
  --swatch-mark: 26px;
  --icon-mark: 22px;

  /* A hard 2px drop, no blur: boards fastened to a frame, not glass floating
     over it. Blur was the previous system's only separator and is gone. */
  --contact-shadow: 0 2px 0 rgba(0, 0, 0, 0.65);
  /* Paper lifted off the desk gets the one soft shadow in the system. */
  --lift-card: 0 4px 14px rgba(10, 6, 4, 0.55);
  --press-travel: 1.5px;

  --motion-press: 80ms;
  --motion-release: 160ms;
  --motion-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Rounded type is the whole reason this reads as warm rather than clinical,
     and it costs nothing: ui-rounded is SF Pro Rounded on Apple platforms and
     Hiragino Maru Gothic is the Japanese rounded face already on the machine.
     No webfont is loaded. Six languages, three of them CJK, and a single
     Japanese webfont is megabytes -- on a page whose job is to paint a sky as
     fast as it can. Where no rounded face exists the stack falls through to the
     platform UI face and the design still holds; the roundness lives in the
     shapes as well as the letters. Latin families come first so Latin glyphs
     are never taken from a CJK font, whose Latin sits on a 1em square. */
  --font-rounded:
    ui-rounded, "Hiragino Maru Gothic ProN", "M PLUS Rounded 1c",
    -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI",
    Roboto, "Helvetica Neue", Arial,
    "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", Meiryo,
    "Pretendard", "Apple SD Gothic Neo", "Malgun Gothic",
    "PingFang SC", "Microsoft YaHei",
    "Noto Sans JP", "Noto Sans KR", "Noto Sans SC",
    system-ui, sans-serif;
  --font-sans: var(--font-rounded);
}
* { box-sizing: border-box; }
/* An element with an explicit `display` beats the UA's [hidden] rule, so
   `el.hidden = true` silently does nothing. That has now bitten three separate
   controls here -- the Energy bar, the sign-in button that stayed after signing
   in, and the invite field -- each time looking like a JS bug rather than a CSS
   one. Stopped at the root instead of per element. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; height: 100%; }
body {
  margin: 0; height: 100%; background: var(--black); color: var(--ink);
  font: 14px/1.5 var(--font-rounded);
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  overflow: hidden;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  /* Numbers here are read as quantities that tick (Energy, Lumen, cost and the
     coordinate labels), so they must not shift width as they change. */
  font-variant-numeric: tabular-nums;
  font-kerning: normal;
}
/* Keyboard focus was invisible before: every control was round and translucent
   on black and the UA outline vanished into it. Candlelight ivory reads on both
   walnut and amber. Pointer focus stays silent. */
:focus-visible {
  outline: var(--line-focus) solid var(--focus);
  outline-offset: var(--focus-offset);
}
.sky-wrap { position: fixed; inset: 0; }
#placeCanvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  touch-action: none; cursor: crosshair; display: block;
}

/* --- The top rail --------------------------------------------------------
   A board fastened across the top of the skylight. It is opaque, so the seven
   controls on it are legible over the Milky Way as well as over empty sky. */
.place-top {
  position: absolute; left: 0; right: 0; top: 0;
  padding: calc(6px + env(safe-area-inset-top)) var(--space-3) 6px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  background: var(--surface-desk);
  border-bottom: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--contact-shadow);
}
.place-brand {
  font-weight: 700; font-size: 15px; letter-spacing: 0.06em;
  color: var(--ink); flex: 0 0 auto;
}
/* At 375px the rail is seven 44px controls wide and there is no room for a
   wordmark; it is the one thing on the rail that is decoration. */
@media (max-width: 620px) { .place-brand { display: none; } }
/* Wrapping, not shrinking. At 375px the seven 44px controls and the sign-in
   button want 444px between them; without a second row the button ran 33px
   past the right edge and the label read "ログインして描". Nothing here can be
   made smaller honestly -- 44px is the tap minimum and the label is the one
   sentence a first-time visitor needs -- so the button takes its own row when
   it has to. Signed in, the account is a 44px peg and the rail is one row
   again. */
.place-top-meta {
  display: flex; align-items: center; gap: var(--space-1);
  flex: 1 1 auto; justify-content: flex-end; flex-wrap: wrap; row-gap: 6px;
}
.place-utility-btn, .place-login { white-space: nowrap; }
/* "L0 · 5 tiles · 10,427 stars" is a developer meter: it names the HEALPix
   level and the tile fetch, which mean nothing to a visitor and take the space
   the sign-in button needs. Kept in the DOM (levelText is still updated, and is
   the quickest way to see what the tile loader is doing in devtools) but never
   shown. */
.place-level { display: none; }
/* Grows so that, on the row of its own it takes at 375px, it still ends at the
   rail's right edge rather than hanging off the left. */
.place-account { display: flex; align-items: center; gap: var(--space-1); flex: 1 1 auto; justify-content: flex-end; }
.place-account-status { font-size: 11px; color: var(--muted); white-space: nowrap; }

/* --- Wooden peg buttons ---------------------------------------------------
   One shape for every secondary control: a 44px target holding a 34px peg with
   a 22px glyph. The target is square and meets the HIG minimum; the peg inside
   is what the eye sees, so the rail does not look like a row of slabs.
   flex-shrink is off deliberately -- under `space-between` these compressed to
   41px, which is how a control silently drops below the tap minimum. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.icon-btn {
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap);
  min-width: var(--tap); min-height: var(--tap);
  padding: 0; border: 0; background: none;
  display: inline-flex; align-items: center; justify-content: center;
  position: relative; color: var(--ink); cursor: pointer;
  transition: color var(--motion-press) ease;
}
.icon-btn::before {
  content: ""; position: absolute; width: 34px; height: 34px;
  background: var(--surface-raised);
  border: var(--line-fine) solid var(--line-wood);
  border-radius: var(--radius-peg);
  box-shadow: var(--contact-shadow);
  transition: background var(--motion-press) ease,
              border-color var(--motion-press) ease,
              transform var(--motion-press) ease,
              box-shadow var(--motion-press) ease;
}
/* Direct child only. The generated avatar is an <svg> too, nested one level
   inside .account-mark, and it carries its own fill and stroke as presentation
   attributes -- which CSS beats. Under a descendant selector it came out as a
   pale outline with no stars: the icon rule had quietly repainted it. */
.icon-btn > svg {
  position: relative; width: var(--icon-mark); height: var(--icon-mark);
  fill: none; stroke: currentColor;
  stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round;
}
.account-mark { display: inline-flex; position: relative; }
.account-mark svg { display: block; }
.icon-btn:hover:not(:disabled)::before {
  background: #382c25; border-color: var(--line-strong);
}
/* Pressed means pressed: the peg goes down and its contact shadow disappears,
   which is what a real key does. Glow is not used as a state. */
.icon-btn:active:not(:disabled)::before {
  transform: translateY(var(--press-travel)); box-shadow: none;
}
.icon-btn:disabled { cursor: default; opacity: 0.38; }
.icon-btn:disabled::before { box-shadow: none; }
/* A toggle that is on is lit at its edge rather than filled, so "on" and
   "hovered" never look the same. */
.icon-btn[aria-pressed="true"] { color: var(--amber); }
.icon-btn[aria-pressed="true"]::before {
  background: var(--surface-card); border-color: var(--amber);
}
/* The report button used to be pink -- the single brightest control on the
   page. A signed-out visitor cannot draw (draw.php needs an account) but CAN
   report (deliberately anonymous), so the loudest thing on screen was also the
   only thing a stranger could do, and it takes work down. Ordinary weight. */
.report-btn { color: var(--muted); }
.account-btn { font: 700 13px/1 var(--font-rounded); }
.account-initial { text-transform: uppercase; }

/* Only shown while signed out, and then it is the primary call to action: it is
   what the visitor came to do, and the only control that leads anywhere. */
.place-login {
  min-height: var(--tap); display: inline-flex; align-items: center;
  padding: 0 var(--space-4); border-radius: var(--radius-hand);
  text-decoration: none; white-space: nowrap;
  background: var(--amber); color: var(--surface-recessed);
  border: var(--line-fine) solid var(--amber-high);
  box-shadow: var(--contact-shadow);
  font: 700 13px/1 var(--font-rounded);
  transition: background var(--motion-press) ease, transform var(--motion-press) ease;
}
.place-login:hover { background: var(--amber-high); }
.place-login:active {
  transform: translateY(var(--press-travel)); box-shadow: none;
}

/* --- The history bar ------------------------------------------------------
   A slip of paper laid on the desk under the top rail. */
.history-bar {
  /* Measured, not assumed: the rail is two rows tall for a signed-out visitor
     on a phone. app.js publishes --place-top-h whenever it can change. */
  position: absolute; top: calc(var(--place-top-h, 64px) + var(--space-1)); left: 50%;
  width: min(460px, calc(100% - 24px)); padding: var(--space-2) var(--space-3);
  transform: translateX(-50%); border-radius: var(--radius-sheet);
  background: var(--surface-card);
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--lift-card);
}
.history-heading, .history-controls { display: flex; align-items: center; gap: var(--space-2); }
.history-heading { justify-content: space-between; color: var(--muted); font-size: 12px; }
.history-heading output { color: var(--ink); }
.history-controls { margin-top: var(--space-1); }
.history-slider { min-width: 0; }
#historySlider { flex: 1 1 auto; min-width: 0; accent-color: var(--amber); cursor: pointer; }
#historySlider:disabled { cursor: default; opacity: 0.38; }
#historyReturn {
  flex: 0 0 auto; min-height: 32px; padding: 0 var(--space-3); border: 0;
  border-radius: var(--radius-peg);
  background: var(--surface-raised); color: var(--amber);
  border: var(--line-fine) solid var(--line-wood);
  font: 700 11px/1 var(--font-rounded); cursor: pointer;
}
.history-mode { display: none; margin: var(--space-2) 0 0; color: var(--correction); font-size: 12px; }
body.is-history .history-bar { border-color: var(--correction); }
body.is-history .history-mode { display: block; }
body.is-history #placeCanvas { cursor: grab; }

/* Transient feedback: what a line cost, or why one was refused. A small card
   set down on the desk, clear of both rails so it never covers a control. */
/* The notes band, not the middle of the sky.
   This sat at top:42% -- dead centre, which is exactly where the line being
   priced is drawn, so "Estimated cost: 1 Energy" landed on top of the thing it
   was describing. It shares the star card's band instead: while a second star
   is pending the card is hidden (see updateStarCard), so this space is free at
   precisely the moment the hint appears, and it is where a reader already
   looks for a sentence about what they just did. */
.place-hint {
  /* Directly below the star card's band, never on top of it. The card begins at
     112px and is at least 64px tall; starting here means the two stack when
     both are up (a toast can fire while a star is selected) instead of one
     covering the other. With the card hidden -- which is the whole of drawing
     mode -- this is still 23% down an 812px phone, well clear of the line being
     drawn. */
  position: absolute; left: 50%;
  top: max(calc(184px + env(safe-area-inset-top)),
           calc(var(--place-card-bottom, 0px) + var(--space-2)));
  z-index: 3;
  transform: translate(-50%, -6px);
  max-width: min(320px, calc(100% - 32px));
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-hand);
  background: var(--surface-card);
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--lift-card);
  font-size: 13px; font-weight: 600; color: var(--ink); text-align: center;
  opacity: 0; pointer-events: none;
  transition: opacity 200ms ease, transform 200ms var(--motion-spring);
}
.place-hint.show { opacity: 1; transform: translate(-50%, 0); }

/* The card is an observation note pinned below the top rail. It occupies the
   sky layer, not the bottom controls: at phone height this keeps selection
   chrome within the existing 25.1% budget. One title line and one detail line
   are intentional; a third would turn looking at a star into a panel. */
.place-star-card {
  /* ESO image credits live at the upper left from 52px down. This begins below
     their tallest one-line credit instead of covering a required attribution. */
  position: absolute; z-index: 2; top: calc(112px + env(safe-area-inset-top)); left: 50%;
  width: min(360px, calc(100% - 24px)); min-height: 64px;
  transform: translateX(-50%); display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  border-radius: var(--radius-sheet); background: var(--surface-card);
  border: var(--line-fine) solid var(--line-wood); box-shadow: var(--lift-card);
}
.place-star-copy { min-width: 0; flex: 1 1 auto; }
.place-star-copy strong, .place-star-copy p {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.place-star-copy strong { color: var(--ink); font: 700 14px/1.3 var(--font-rounded); }
.place-star-copy p { margin: 2px 0 0; color: var(--muted); font: 12px/1.35 var(--font-rounded); }
.star-info-draw { flex: 0 0 auto; color: var(--surface-recessed); }
.star-info-draw::before { background: var(--amber); border-color: var(--amber-high); }
.star-info-draw:hover:not(:disabled)::before { background: var(--amber-high); }
.star-info-draw.is-active::before { background: var(--surface-raised); border-color: var(--amber); }
.star-info-draw.is-active { color: var(--amber); }
.place-deepsky-card { align-items: flex-start; }
.place-deepsky-card .place-star-copy p {
  overflow: visible; text-overflow: clip; white-space: normal;
}
/* A line row that the armed eraser will take on one tap. Same shape as the
   other rows so the list does not jump, but in the correction colour -- the
   row's job changed, and the only signal is what it says and how it reads. */
.place-edge-link.is-erasable {
  border-color: var(--correction);
  color: var(--correction);
}
/* The armed eraser, on the button itself. aria-pressed carries it for screen
   readers; this is the same state for everyone else. */
body.is-erasing #placeEraser {
  border-color: var(--correction);
  color: var(--correction);
}

/* Erasing your own line. Same shape as the card's other button so the card
   stays one column, but in the correction colour: this one takes something
   away, and it should not read as the neutral "back". */
.place-edge-erase {
  display: block; width: 100%; min-height: var(--tap); margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: var(--line-fine) solid var(--correction);
  border-radius: var(--radius-hand); background: var(--surface-recessed);
  color: var(--correction); font: 12px/1.35 var(--font-rounded);
  text-align: left; overflow-wrap: anywhere; cursor: pointer;
}
.place-edge-erase:hover { background: var(--surface-raised); }
.place-edge-erase:disabled { opacity: 0.55; cursor: default; }
.place-edge-erase:focus-visible {
  outline: var(--line-fine) solid var(--amber-high); outline-offset: 2px;
}

.place-deepsky-link, .place-deepsky-back {
  display: block; width: 100%; min-height: var(--tap); margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3); border: var(--line-fine) solid var(--line-wood);
  border-radius: var(--radius-hand); background: var(--surface-recessed);
  color: var(--amber-high); font: 12px/1.35 var(--font-rounded);
  text-align: left; overflow-wrap: anywhere; cursor: pointer;
}
.place-deepsky-link:hover, .place-deepsky-back:hover { background: var(--surface-raised); }
.place-deepsky-link:focus-visible, .place-deepsky-back:focus-visible {
  outline: var(--line-focus) solid var(--focus); outline-offset: var(--focus-offset);
}
.place-star-edges { display: grid; gap: var(--space-1); margin-top: var(--space-2); }
.place-edge-link {
  display: block; width: 100%; min-height: var(--tap); padding: var(--space-2) var(--space-3);
  border: var(--line-fine) solid var(--line-wood); border-radius: var(--radius-hand);
  background: var(--surface-recessed); color: var(--amber-high);
  font: 12px/1.35 var(--font-rounded); text-align: left; cursor: pointer;
}
.place-edge-link:hover { background: var(--surface-raised); }
.place-edge-link:focus-visible {
  outline: var(--line-focus) solid var(--focus); outline-offset: var(--focus-offset);
}
.place-deepsky-credit {
  margin-top: var(--space-1); color: var(--dim);
  font: 11px/1.35 var(--font-rounded); overflow-wrap: anywhere;
}
.place-deepsky-credit a { color: var(--dim); }

/* --- The bottom rail ------------------------------------------------------
   The near edge of the desk: the meter, the tray of colours, the tools. */
.place-bottom {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--space-2) var(--space-3) calc(4px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: var(--space-1);
  background: var(--surface-desk);
  border-top: var(--line-fine) solid var(--line-wood);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.45);
}
/* The rail was laid out at 375px, where `space-between` puts the colours at one
   thumb and the actions at the other and everything is reachable. On a 1728px
   window the same rule pushes them 1,500px apart: the colours and the widths
   sit alone in the bottom-left corner, Draw / link / undo / name sit in the
   bottom-right, and nothing connects them. Reported as "I cannot find the name
   button" -- correctly, because it was a screen away from the drawing.
   The desk is a desk, not the width of the monitor. */
.place-bottom > * { width: 100%; max-width: 760px; margin: 0 auto; }

/* Energy is a count of available strokes. The precise count stays visible, and
   the bar makes the next regeneration glanceable without implying distance.
   "27 / 30 Energy · full in 1m30s / Lumen 0 · 2,877 left today" was four facts
   in two sentences permanently on screen; it is a groove and two numbers now,
   and the rest is in the title attribute and the Lumen card. */
.budget { display: flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-1); }
.budget-track {
  flex: 1 1 auto; height: 10px; border-radius: 5px;
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
  overflow: hidden;
}
.budget-fill {
  height: 100%; width: 100%;
  background: linear-gradient(90deg, #d48b28, var(--amber));
  transition: width 400ms ease;
}
.budget-readout { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-1); }
.budget-icon { width: 16px; height: 16px; flex: 0 0 auto; fill: var(--amber); stroke: none; }
.budget-text { flex: 0 0 auto; font: 700 12px/1 var(--font-rounded); color: var(--ink); }
.budget-meta { font-size: 11px; color: var(--dim); }
/* The wait, next to the count it belongs to. Dim on purpose: it answers a
   question you only ask when the bar is empty, and it must not compete with
   the two numerals that say where you stand. */
.budget-next {
  flex: 0 0 auto; font: 600 11px/1 var(--font-rounded); color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* The tray and the tools. At 375px a full row of 44px targets does not fit on
   one line -- five colours plus cancel, undo and Draw is 466px of controls --
   so the row wraps and both halves keep their tap minimum. Shrinking the
   targets to make one line fit is the trade the old 30px swatches made, and it
   is the wrong one on a touch surface. */
.place-tools {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--space-2);
}
.colors { display: flex; align-items: center; gap: var(--space-1); flex: 0 1 auto; }
.place-actions { display: flex; align-items: center; gap: var(--space-1); flex: 0 0 auto; margin-left: auto; }
/* Walnut like the other tools, not amber. An amber rule was written here first
   and never applied -- .tool-btn is declared later and won -- and once that was
   noticed, keeping it would have put a second loud button next to the amber
   Draw tick. One primary at a time. */
.place-name-work-action {
  display: inline-flex; align-items: center; gap: var(--space-1);
}
.place-name-work-action > svg { flex: 0 0 auto; width: 16px; height: 16px; fill: currentColor; }
/* The colour swatches are the one place the palette is the content: a gem set
   in a socket in the desk. 50% radius is reserved for these because they are
   star discs, not furniture. */
.color-dot {
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap); min-width: var(--tap);
  padding: 0; border: 0; background: none; border-radius: var(--radius-peg);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--c);
}
.color-dot::before {
  content: ""; width: var(--swatch-mark); height: var(--swatch-mark);
  border-radius: var(--radius-star); background: var(--c);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  transition: transform var(--motion-spring), box-shadow var(--motion-spring);
}
.color-dot:hover:not(:disabled)::before { transform: scale(1.1); }
.color-dot:disabled { cursor: default; opacity: 0.34; }
.color-dot.on::before {
  outline: 2px solid var(--amber); outline-offset: 2px;
  box-shadow: 0 0 8px rgba(245, 182, 66, 0.4);
}
/* A recessed slot, like the colour swatches sit in. Three unlabelled hairlines
   floating on the desk read as decoration -- they were on production for a day
   and were not found at all. The slot says "these three belong together and one
   of them is chosen". */
.widths {
  display: flex; gap: var(--space-1); flex: 0 0 auto;
  padding: 0 var(--space-1);
  border-radius: var(--radius-hand);
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
}
.width-dot {
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap); min-width: var(--tap);
  padding: 0; border: 0; background: none; border-radius: var(--radius-peg);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
}
/* A sample of the stroke itself, not a numeral: the control shows what the line
   will look like, in the shape the canvas draws it. The three samples keep one
   length and differ only in weight, so the row reads as one line getting
   heavier rather than as three unrelated shapes -- an earlier version made the
   thickest a 7px capsule, and it read as a pill, not a stroke.

   Unselected is dim. The selected one is the bright one, exactly like the
   colour dots above it; before this the heaviest sample was the loudest thing
   in the row whether or not it was chosen. */
.width-dot i {
  display: block; width: 22px; height: var(--w);
  border-radius: 999px; background: var(--ink); opacity: 0.55;
  transition: transform var(--motion-spring), opacity var(--motion-spring),
              background var(--motion-spring);
}
.width-dot:hover:not(:disabled) i { transform: scaleX(1.15); opacity: 1; }
.width-dot:disabled { cursor: default; opacity: 0.34; }
.width-dot.on i { background: var(--amber); opacity: 1; box-shadow: 0 0 8px rgba(245, 182, 66, 0.4); }

/* The emblem takes the ink colour like every other symbol here: one weight,
   one colour, the same 24-grid. That constraint is what makes a set of sixty
   read as designed rather than collected. */
.alliance-identity { display: flex; align-items: center; gap: var(--space-2); }
.alliance-identity h3 { margin: 0; min-width: 0; }
.alliance-emblem {
  flex: 0 0 auto; width: 28px; height: 28px;
  fill: none; stroke: var(--amber);
  stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round;
}
.emblem-picker { margin-top: var(--space-3); }
.emblem-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--tap), 1fr));
  gap: var(--space-1); margin-top: var(--space-2);
}
.emblem-choice {
  width: var(--tap); height: var(--tap); min-width: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  border-radius: var(--radius-peg);
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
}
.emblem-choice svg {
  width: 20px; height: 20px;
  fill: none; stroke: var(--ink); opacity: 0.72;
  stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round;
}
.emblem-choice:hover:not(:disabled) svg { opacity: 1; }
.emblem-choice.on { border-color: var(--amber); }
.emblem-choice.on svg { stroke: var(--amber); opacity: 1; }
.emblem-choice:disabled { cursor: default; opacity: 0.4; }

.tool-btn {
  height: var(--tap); min-height: var(--tap); padding: 0 var(--space-3);
  border-radius: var(--radius-hand);
  background: var(--surface-raised); color: var(--ink);
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--contact-shadow);
  font: 700 13px/1 var(--font-rounded); cursor: pointer; white-space: nowrap;
  transition: background var(--motion-press) ease, transform var(--motion-press) ease;
}
.tool-btn:hover:not(:disabled) { background: #382c25; }
.tool-btn:active:not(:disabled) { transform: translateY(var(--press-travel)); box-shadow: none; }
.tool-btn:disabled { cursor: default; opacity: 0.38; }
/* The one primary action on the desk: confirming the line. Amber is spent here
   and on the current value, nowhere else. */
.tool-btn-draw {
  background: var(--amber); color: var(--surface-recessed);
  border-color: var(--amber-high);
}
.tool-btn-draw:hover:not(:disabled) { background: var(--amber-high); }
/* Taking over someone else's line is not the same act as drawing a new one and
   must not wear the same colour. Terracotta is the correction mark. */
.tool-btn-draw.is-takeover {
  background: var(--correction); color: var(--surface-recessed);
  border-color: #f0a08a;
}
.tool-btn-draw.is-takeover:hover:not(:disabled) { background: #f0a08a; }
.place-tools.is-disabled { opacity: 0.72; }

/* Confirming the line is the one primary action on the desk, and it is the one
   control that carries a number -- the cost, which has to be read before
   committing -- so it is the only peg allowed to grow wider than its target. */
.icon-btn-primary { width: auto; min-width: var(--tap); padding: 0 var(--space-3); }
.icon-btn-primary::before {
  width: 100%; height: 34px; border-radius: var(--radius-hand);
  background: var(--amber); border-color: var(--amber-high);
}
.icon-btn-primary { color: var(--surface-recessed); }
.icon-btn-primary:hover:not(:disabled)::before { background: var(--amber-high); border-color: var(--amber-high); }
.icon-btn-primary .cost {
  position: relative; margin-left: var(--space-1);
  font: 700 13px/1 var(--font-rounded);
}
/* Smaller than the check, and riding with the numeral rather than beside it:
   it is a unit, not a second action. */
.icon-btn-primary .cost-unit {
  position: relative; width: 13px; height: 13px; margin-left: 1px;
  flex: 0 0 auto; opacity: 0.85;
}
/* Taking over someone else's line is not the same act as drawing a new one and
   must not wear the same colour. Terracotta is the correction mark. */
.icon-btn-primary.is-takeover::before { background: var(--correction); border-color: #f0a08a; }
.icon-btn-primary.is-takeover:hover:not(:disabled)::before { background: #f0a08a; }

/* --- Lumen ---------------------------------------------------------------- */
.lumen-panel {
  width: min(720px, 100%); align-self: center; padding: var(--space-3);
  border-radius: var(--radius-sheet); background: var(--surface-card);
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--lift-card);
}
.lumen-heading {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 2px var(--space-2); color: var(--amber); font-size: 12px; font-weight: 700;
}
.lumen-heading span { color: var(--ink); }
.lumen-choices { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
.lumen-choice {
  min-width: 0; min-height: 58px; padding: var(--space-2) var(--space-3); border: 0;
  border-radius: var(--radius-hand);
  background: var(--surface-raised); color: var(--ink); text-align: left;
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--contact-shadow); cursor: pointer;
  transition: background var(--motion-press) ease, transform var(--motion-press) ease;
}
.lumen-choice strong, .lumen-choice span { display: block; }
.lumen-choice strong { font: 700 13px/1.35 var(--font-rounded); }
.lumen-choice span { margin-top: 3px; color: var(--muted); font: 11px/1.35 var(--font-rounded); }
.lumen-choice:hover:not(:disabled) { background: #382c25; }
.lumen-choice:active:not(:disabled) { transform: translateY(var(--press-travel)); box-shadow: none; }
.lumen-choice:disabled, .lumen-choice.is-unavailable { cursor: default; opacity: 0.45; }
/* Where Lumen comes from. Without this the panel is a dead end: two things you
   cannot afford and no way to earn any -- and because Energy refills on its
   own, the reasonable guess is that Lumen does too. */
/* The definition sits ABOVE the two prices: reading "Lumen buys Energy" after
   being shown two amounts you cannot afford is reading it too late. */
.lumen-what { margin: 0 2px var(--space-2); font-size: 12px; color: var(--muted); line-height: 1.5; }
/* Earning is its own block with its own heading, because the invite link used
   to appear as a bare URL under a sentence about buying, with nothing saying
   the two were related. */
.lumen-earn { margin-top: var(--space-3); border-top: var(--line-fine) solid var(--line-wood); padding-top: var(--space-3); }
.lumen-earn h4 { margin: 0 2px var(--space-2); font-size: 12px; font-weight: 600; color: var(--muted); }
.lumen-earn p { margin: 0 2px; font-size: 12px; color: var(--muted); line-height: 1.5; }
.lumen-later { margin-top: var(--space-2) !important; opacity: .75; }
.lumen-invite { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.lumen-invite input {
  flex: 1; min-width: 0; font: 16px ui-monospace, SFMono-Regular, Menlo, monospace;
  min-height: var(--tap); padding: 0 var(--space-3); border-radius: var(--radius-hand);
  color: var(--ink); background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
}
.lumen-invite button {
  flex: 0 0 auto; font: 700 12px/1 var(--font-rounded);
  min-height: var(--tap); padding: 0 var(--space-3);
  border-radius: var(--radius-hand); cursor: pointer; color: var(--ink);
  background: var(--surface-raised); border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--contact-shadow);
}
/* The ad row is a third energy action, so it reads like the two above it --
   but full width, because it is not one of a pair and a half-width button
   beside nothing looks like a missing sibling. */
.lumen-ad {
  width: 100%; margin-top: var(--space-2);
  display: block; min-height: var(--tap);
}
.lumen-ad strong { display: block; }
.lumen-ad span { display: block; font-size: 12px; color: var(--muted); }

.lumen-buy { margin-top: var(--space-3); }
.lumen-buy strong { display: block; font-size: 12px; color: var(--muted); font-weight: 600; margin-bottom: var(--space-2); }
.lumen-bundles { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.lumen-bundle {
  flex: 1 1 auto; min-width: 92px; min-height: var(--tap);
  font: inherit; padding: var(--space-2) var(--space-3); cursor: pointer;
  border-radius: var(--radius-hand); color: var(--ink); text-align: left; line-height: 1.35;
  background: var(--surface-raised); border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--contact-shadow);
}
.lumen-bundle b { display: block; font-size: 13px; color: var(--amber); }
.lumen-bundle span { font-size: 11px; color: var(--muted); }
.lumen-bundle:disabled { opacity: 0.5; cursor: default; }

.feature-status { min-height: 0; margin-top: 0; font-size: 12px; color: var(--success); }
.feature-status:not(:empty) { min-height: 1.35em; margin-top: var(--space-2); }
.feature-status.is-error { color: var(--correction); }

/* --- Folios: dialogs and cards --------------------------------------------
   Heavy paper laid on the desk. 20px corners because that is how thick rag
   paper is trimmed, and the one soft shadow in the system because paper lifted
   off a surface is the only thing here that floats. */
.feature-dialog {
  width: min(520px, calc(100% - 24px)); max-height: min(720px, calc(100% - 24px));
  padding: var(--space-5); border: var(--line-fine) solid var(--line-wood);
  border-radius: var(--radius-sheet); overflow: auto;
  background: var(--surface-card); color: var(--ink);
  box-shadow: var(--lift-card); font-family: var(--font-rounded); user-select: text;
}
.feature-dialog::backdrop { background: rgba(3, 2, 1, 0.72); }

/* The report dialog moves OFF the middle and its backdrop goes nearly clear.
   It is the one dialog whose subject is the sky behind it: the region and the
   lines about to be hidden are drawn at the centre, and a centred card over a
   72%-black backdrop hid exactly the thing the reader had to check. */
.report-dialog {
  margin-block: auto 0;
  margin-bottom: max(var(--space-4), env(safe-area-inset-bottom));
  width: min(420px, calc(100% - 24px));
}
.report-dialog::backdrop { background: rgba(3, 2, 1, 0.28); }
.report-count {
  margin: var(--space-2) 0 0; font: 700 14px/1.4 var(--font-rounded);
  color: var(--correction); font-variant-numeric: tabular-nums;
}
/* Nothing to take is not an error, but it must not read as a green light. */
.report-count.is-empty { color: var(--muted); font-weight: 600; }
.dialog-heading { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.dialog-heading h2 { margin: 0; font-size: 19px; font-weight: 700; }
.dialog-close {
  min-height: var(--tap); min-width: var(--tap); padding: 0 var(--space-3); border: 0;
  border-radius: var(--radius-hand);
  background: var(--surface-raised); color: var(--muted); cursor: pointer;
  border: var(--line-fine) solid var(--line-wood);
  font: 700 12px/1 var(--font-rounded);
}
.alliance-none, .alliance-current p, .feature-note { color: var(--muted); }
.alliance-current h3 { margin: var(--space-5) 0 2px; font-size: 21px; font-weight: 700; }
.alliance-current h4 { margin: var(--space-4) 0 var(--space-2); font-size: 12px; color: var(--muted); }
.member-list {
  display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-4); padding: 0; list-style: none;
}
.member-list li {
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-peg);
  background: var(--surface-raised); border: var(--line-fine) solid var(--line-wood);
  font-size: 12px;
}
.alliance-forms { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); margin-top: var(--space-4); }
.feature-form {
  min-width: 0; padding: var(--space-3); border-radius: var(--radius-hand);
  background: var(--surface-raised); border: var(--line-fine) solid var(--line-wood);
}
.feature-form h3 { margin: 0 0 var(--space-2); font-size: 13px; font-weight: 700; }
.form-row { display: flex; gap: var(--space-2); }
/* Inputs are grooves cut in the desk, not raised buttons: a place to write. */
.form-row input {
  min-width: 0; flex: 1 1 auto; min-height: var(--tap); padding: 0 var(--space-3);
  border-radius: var(--radius-hand); color: var(--ink);
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
  font: 16px var(--font-rounded);
}
.dialog-action {
  min-height: var(--tap); padding: 0 var(--space-4); border: 0;
  border-radius: var(--radius-hand);
  background: var(--amber); color: var(--surface-recessed);
  border: var(--line-fine) solid var(--amber-high);
  box-shadow: var(--contact-shadow);
  font: 700 13px/1 var(--font-rounded); cursor: pointer;
  transition: background var(--motion-press) ease, transform var(--motion-press) ease;
}
.dialog-action:hover:not(:disabled) { background: var(--amber-high); }
.dialog-action:active:not(:disabled) { transform: translateY(var(--press-travel)); box-shadow: none; }
.dialog-action.danger {
  background: var(--correction); border-color: #f0a08a; color: var(--surface-recessed);
}
.dialog-action.danger:hover:not(:disabled) { background: #f0a08a; }
.dialog-action:disabled { cursor: default; opacity: 0.45; }
.feature-note { margin: var(--space-4) 0 0; font-size: 12px; line-height: 1.6; }
.report-explain { margin-top: var(--space-3); font-size: 14px; }
.dialog-buttons { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-5); }

/* Attribution for the star catalogue, shown the way a map credits OSM. It sits
   on the bottom rail rather than over the sky, so it is legible on every
   background and never lands under the colour tray.
   The full attribution, including the wording the licences require verbatim,
   is on credits.html, which this line links to and which
   tests/place-credits.test.mjs holds to its exact text. The short form here is
   what fits on the rail: at 11px the long form wrapped to three lines and took
   50px of the 812 the sky has. */
.place-credit {
  margin: 0; padding: 0 var(--space-1);
  font-size: 10px; color: var(--dim); text-align: center; line-height: 1.35;
}
.place-credit a { color: inherit; }

/* --- The first-visit rules card ------------------------------------------ */
/* These six are <dialog> elements opened with showModal(), which is where
   Escape, the focus trap, returning focus to whatever opened them, and making
   the page behind inert all come from. None of that was implemented by hand
   here, and writing it six times is how five of them end up subtly different.
   The two feature dialogs (Alliance, report) were already real dialogs; this
   makes all eight the same kind of thing.

   The dialog is the full-viewport centring frame, so it is transparent and the
   dimming moves to ::backdrop -- the browser's own layer, which is what keeps
   it behind the card and above everything else without a z-index race. */
dialog.place-rules {
  position: fixed; inset: 0; z-index: 30;
  width: auto; height: auto; max-width: none; max-height: none;
  margin: 0; border: 0; padding: var(--space-5);
  background: transparent; color: inherit; overflow: visible;
  align-items: center; justify-content: center;
}
dialog.place-rules[open] { display: flex; }
dialog.place-rules::backdrop { background: rgba(3, 2, 1, 0.76); }
.place-rules-card {
  width: min(420px, 100%); max-height: 100%; overflow-y: auto;
  padding: var(--space-5); border-radius: var(--radius-sheet);
  background: var(--surface-card);
  border: var(--line-fine) solid var(--line-wood);
  box-shadow: var(--lift-card);
}
.place-rules-card h2 { margin: 0 0 var(--space-3); font-size: 18px; font-weight: 700; color: var(--ink); }
.place-rules-card ul { margin: 0; padding: 0 0 0 var(--space-5); }
.place-rules-card li { margin: 0 0 var(--space-3); font-size: 14px; line-height: 1.7; color: var(--ink); }
.place-rules-note {
  margin: var(--space-3) 0 0; padding-top: var(--space-3);
  border-top: var(--line-fine) solid var(--line-wood);
  font-size: 12px; line-height: 1.6; color: var(--muted);
}
.place-rules-start {
  margin-top: var(--space-4); width: 100%; min-height: var(--tap); border: 0;
  border-radius: var(--radius-hand); cursor: pointer;
  background: var(--amber); color: var(--surface-recessed);
  border: var(--line-fine) solid var(--amber-high);
  box-shadow: var(--contact-shadow);
  font: 700 14px/1 var(--font-rounded);
  transition: background var(--motion-press) ease, transform var(--motion-press) ease;
}
.place-rules-start:hover { background: var(--amber-high); }
.place-rules-start:active { transform: translateY(var(--press-travel)); box-shadow: none; }
/* Secondary weight: sign out and close must not compete with Save. */
.place-rules-start.is-quiet {
  background: var(--surface-raised); color: var(--muted);
  border-color: var(--line-wood);
}
.place-rules-start.is-quiet:hover { background: #382c25; }

/* Provider buttons inside the sign-in card. Google and Apple each render their
   own control and neither may be restyled, so this only gives them room and a
   consistent centre line. */
.signin-providers {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-1); min-height: var(--tap);
}
/* Apple's SDK sizes its button from the container, so an unconstrained column
   produced a control several times the height of Google's that covered the
   close button. Both providers are pinned to the same width instead. */
.signin-providers > div { width: 100%; max-width: 240px; display: flex; justify-content: center; }
#appleid-signin { max-height: var(--tap); overflow: hidden; }

/* --- Account card --------------------------------------------------------- */
.account-card { position: relative; }
/* Close belongs in the corner, not as a third full-width button competing with
   Save and Sign out for the same glance. */
.card-close {
  position: absolute; top: var(--space-2); right: var(--space-2);
  width: var(--tap); height: var(--tap); padding: 0; border: 0;
  border-radius: var(--radius-peg);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--muted); cursor: pointer;
  transition: background var(--motion-press) ease, color var(--motion-press) ease;
}
.card-close svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round;
}
.card-close:hover { background: var(--surface-raised); color: var(--ink); }
.account-identity {
  display: flex; align-items: center; gap: var(--space-3);
  padding-bottom: var(--space-4); margin-bottom: var(--space-4);
  border-bottom: var(--line-fine) solid var(--line-wood);
}
/* The generated figure sits in a socket: recessed rather than raised, so it
   reads as a portrait rather than another button. */
.account-avatar {
  flex: 0 0 auto; width: 66px; height: 66px; border-radius: var(--radius-star);
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
}
.account-avatar svg { display: block; }
/* flex: 1 so it takes the row's leftover width.
   Without it this box sized to its longest word, and the XP track inside --
   which is flex: 1 of THIS box -- collapsed to a few pixels. The bar rendered
   as a dot, which is what it looked like on screen: a progress bar with no
   room to show progress. */
.account-names { flex: 1 1 0; min-width: 0; }
.account-names h2 { margin: 0 0 3px; font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: 0.03em; }
.account-handle { margin: 0; font-size: 19px; font-weight: 700; color: var(--ink); word-break: break-word; line-height: 1.25; }
.account-progression { margin: 4px 0 0; font-size: 12px; line-height: 1.35; color: var(--amber); }
.account-field { display: block; margin: 0 0 var(--space-1); }
.account-field span { display: block; margin-bottom: var(--space-2); font-size: 12px; color: var(--muted); }
.account-field-row { display: flex; gap: var(--space-2); }
/* 16px, and not a pixel less, on every field somebody types into.
   Mobile Safari zooms the page when a focused input is smaller than 16px, and
   there is no way to decline: the alternative -- maximum-scale=1 on the
   viewport -- takes pinch-zoom away from everyone who needs it, all the time,
   to fix a keyboard. Reported 2026-09-06: opening the keyboard to name a work
   blew the canvas up and left the layout displaced afterwards.
   assets/style.css already does this for the share code and URL fields. */
.account-field input {
  flex: 1 1 auto; min-width: 0; min-height: var(--tap); padding: 0 var(--space-3);
  border-radius: var(--radius-hand); color: var(--ink);
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood);
  font: 16px/1 var(--font-rounded);
  transition: border-color var(--motion-press) ease;
}
.account-field input:focus { outline: none; border-color: var(--amber); }
.account-field-row .place-rules-start { margin-top: 0; width: auto; padding: 0 var(--space-4); }
.account-signout { margin-top: var(--space-3); }
.work-name-field { display: block; margin-top: var(--space-4); }
.work-name-field span {
  display: block; margin-bottom: var(--space-2); font-size: 12px; color: var(--muted);
}
.work-name-field input {
  width: 100%; min-height: var(--tap); padding: 0 var(--space-3);
  border-radius: var(--radius-hand); color: var(--ink);
  background: var(--surface-recessed); border: var(--line-fine) solid var(--line-wood);
  font: 16px/1 var(--font-rounded);
}
.work-name-field input:focus { outline: none; border-color: var(--amber); }

/* --- The way to the other half of the site -------------------------------
   place and the main app were two sites that never mentioned each other: the
   only link was the words "Draw Your Sky" in the credit line, where it reads
   as an attribution. Quiet, one line, and dismissible -- somebody who does not
   want the private sky should not be asked twice. */
.sibling-link {
  display: flex; align-items: center; gap: var(--space-1);
  margin: var(--space-2) auto 0; width: fit-content; max-width: 100%;
  border-radius: var(--radius-hand);
  background: var(--surface-raised);
  border: var(--line-fine) solid var(--line-wood);
}
.sibling-body {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 34px; padding: 0 var(--space-1) 0 var(--space-3);
  color: var(--ink); text-decoration: none;
  font: 600 12px/1.3 var(--font-rounded);
  min-width: 0;
}
.sibling-body:hover { color: var(--amber); }
.sibling-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sibling-icon { width: 16px; height: 16px; flex: 0 0 auto; fill: var(--amber); }
.sibling-arrow {
  width: 14px; height: 14px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* A 34px target rather than the usual 44: this is a dismissal, and giving it
   the same weight as the drawing controls would be the wrong emphasis. It sits
   away from them, so the smaller target costs nothing in misses. */
.sibling-close {
  flex: 0 0 auto; width: 30px; height: 34px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; color: var(--muted);
  border-radius: 0 var(--radius-hand) var(--radius-hand) 0;
}
.sibling-close:hover { color: var(--ink); background: var(--surface-recessed); }
.sibling-close svg {
  width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round;
}

/* --- The tracing guide ---------------------------------------------------
   A file picker that reads like a button, and three sliders. Deliberately
   plain: this panel is scaffolding for the drawing, not part of it. */
.ref-image-private { color: var(--muted); font-size: 12px; }
.ref-image-choose {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap); margin: var(--space-3) 0 var(--space-2);
  border-radius: var(--radius-hand); cursor: pointer;
  background: var(--surface-raised);
  border: var(--line-fine) solid var(--line-wood);
  color: var(--ink); font: 600 14px/1 var(--font-rounded);
}
.ref-image-choose:hover { background: #382c25; }
/* The input is the whole label's hit area; hiding it visually keeps the native
   file dialog while letting the label look like the rest of the card. */
.ref-image-choose input[type="file"] {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  overflow: hidden; clip: rect(0 0 0 0);
}
.ref-image-slider { display: block; margin: var(--space-3) 0; }
.ref-image-slider span {
  display: block; margin-bottom: var(--space-1);
  font-size: 12px; color: var(--muted);
}
.ref-image-slider input[type="range"] {
  width: 100%; min-height: var(--tap); accent-color: var(--amber);
}
#refImageControls .tool-btn { width: 100%; margin-top: var(--space-2); }
.ref-image-remove { color: var(--correction); }

/* --- Language card -------------------------------------------------------- */
.language-list { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); margin: var(--space-1) 0 2px; }
.language-choice {
  min-height: var(--tap); padding: 0 var(--space-3); border: 0;
  border-radius: var(--radius-hand);
  background: var(--surface-raised);
  border: var(--line-fine) solid var(--line-wood);
  color: var(--ink); font: 600 14px/1 var(--font-rounded);
  cursor: pointer; text-align: center;
}
.language-choice:hover { background: #382c25; }
.language-choice.on {
  background: var(--amber); color: var(--surface-recessed); border-color: var(--amber-high);
}

@media (max-width: 620px) {
  .place-top { padding-left: var(--space-2); padding-right: var(--space-2); }
  .place-top-meta { gap: 0; justify-content: space-between; width: 100%; }
  .place-account-status { display: none; }
  .place-bottom { padding-left: var(--space-2); padding-right: var(--space-2); }
  .alliance-forms { grid-template-columns: 1fr; }
  .feature-dialog { padding: var(--space-4); }
}

/* Level progress. Same amber-on-recessed reading as the energy bar, at a
   smaller weight: this one is a status, not a control. */
.account-xp { margin-top: var(--space-2); display: flex; align-items: center; gap: var(--space-2); }
.account-xp-track {
  flex: 1; min-width: 0; height: 6px; border-radius: 3px;
  background: var(--surface-recessed);
  border: var(--line-fine) solid var(--line-wood); overflow: hidden;
}
.account-xp-fill {
  height: 100%; width: 0; border-radius: 3px; background: var(--amber);
  transition: width var(--motion-press) ease;
}
.account-xp-text {
  flex: 0 0 auto; font: 600 11px/1 var(--font-rounded); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
