/* studio.css — the emblem studio.
 *
 * The materials are the trunk form's, on purpose: the studio is a room inside
 * the same session and it should read as another piece of the same page rather
 * than as a second application. Aged parchment for the workbench, brass for the
 * one control that commits, and the tag's own handwriting throughout.
 *
 * WHAT IS DELIBERATELY NOT HERE: the size of the drawable square. studio.js
 * measures the stage and writes the side length onto #st-canvas, because the
 * square is a thing the tools have to agree with — a CSS-only square would put
 * the number in two places and only one of them would be right. See the long
 * note at the top of studio.js.
 */

/* ---------- The ladder ----------
   THREE OF THESE RUNGS ARE THIS FILE'S — .st-backdrop, .st-panel and
   .st-cursors — and they are three sibling elements at the top of the document,
   so each number is the number it actually renders at rather than one trapped
   inside a parent's stacking context. The rest of the list is the neighbourhood
   they sit in: the rung immediately below (the form they are raised over), the
   one that had to move to clear them, and the two that must still beat them.
   The sentence used to read "Three rungs" and then name seven, which is the
   sort of thing a reader checks once and then stops trusting the comment. (The
   first attempt at this correction said six. Count the rows.)

     7500   .trunk-form-backdrop        the form, which stays mounted behind
     7800   .st-backdrop
     7900   .st-panel
     7950   .st-cursors
     7960   .hint.notice                trunks.css — the page's one line of
                                        prose to the user, which has to be
                                        legible OVER the canvas: two of the
                                        four notices it carries while the
                                        studio is up are the only feedback
                                        their case has (see .hint.notice in
                                        trunks.css). It was 7600 — chosen
                                        against the form's backdrop, before
                                        this file existed — and was therefore
                                        painted behind the studio's blur
     9999   .polaroid.dragging
     10000  .lock-screen

   The full page ladder lives in trunks.css under `.trunk.lifted`. Nothing in
   it moves for the studio: these three sit in the gap the form's 7500 and the
   dragging photo's 9999 already left. The notice's rung is the one number that
   moved for the studio, and it moved because it had to clear it. */

.st-backdrop {
  position: fixed;
  inset: 0;
  z-index: 7800;
  background: rgba(20, 12, 4, 0.62);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: st-fade 0.22s ease both;
}
/* ---------- `hidden` HIDES ----------
   One rule, and it is what makes the attribute work for every element in this
   studio — including the ones that do not exist yet.

   THE TRAP IT CLOSES HAS SPRUNG TWICE ON THIS BRANCH. The UA stylesheet's
   `[hidden] { display: none }` loses to any author `display` on ORIGIN, before
   specificity is consulted at all, so `.st-panel { display: flex }` would leave
   the panel on screen with `hidden` set, and `.st-group { display: flex }` meant
   that shutting a tool group did nothing whatsoever — the fill toggle stayed
   visible, and clickable, for the pen. Both were found by hand, one task apart.

   The per-element answer — a `.st-thing[hidden] { display: none }` beside each
   block — is what failed both times, because it is a thing to REMEMBER. This is
   the same fact with one home instead of seven.

   IT WINS BY SPECIFICITY, not by order: every selector here is (1,1,0), one id
   plus one attribute, and the strongest `display` rule anywhere in this file is
   (0,1,1) (`.st-canvas svg`). It is an ordinary declaration and not
   `!important`, so it stops applying the moment the attribute comes off and an
   element shown again gets whatever `display` its own block gives it.

   THE THREE IDS ARE THE STUDIO'S THREE ROOTS. index.html puts the backdrop, the
   panel and the cursor layer at the top of the document as siblings — the
   z-index ladder above is why — so no single selector reaches all three.
   Everything else the studio hides is inside the panel (.st-offline, .st-note,
   .st-group, .st-face) and Task 14's cursors will be inside the cursor layer, so
   the two descendant selectors cover the rest at any depth, today and later.

   WHAT ELSE THE DESCENDANT SELECTORS CATCH: nothing that is meant to stay on
   screen. `[hidden]` matches the attribute itself and not `aria-hidden`, which
   is the only one the markup underneath uses — trunks.js writes `aria-hidden` on
   the preview trunk's parts and Drawings.toSvg writes neither, so the preview
   and the canvas are untouched. An element that wants to be seen does not carry
   the attribute; that is the whole meaning of it.

   THE PER-CLASS RULES ARE GONE rather than kept as belt and braces, and that is
   a decision. They were worse than redundant: the note that used to stand here
   told the next author that adding a `display` to any of those blocks would
   silently stop `hidden` from hiding, which this rule now makes untrue — and a
   file carrying seven copies of a fact plus a paragraph explaining a failure
   that can no longer happen is how the pattern that failed twice gets taught to
   the next reader. tests/studio.test.js holds this rule STRUCTURALLY: it
   re-derives every `display` declaration in this file and fails if any of them
   ever reaches (1,1,0), so a class block added tomorrow cannot quietly outrank
   it and a hidden element added tomorrow needs no rule of its own. */
#studio-backdrop[hidden],
#studio-panel[hidden],
#studio-cursor-layer[hidden],
#studio-panel [hidden],
#studio-cursor-layer [hidden] { display: none; }

.st-panel {
  position: fixed;
  /* Centred by auto margins rather than by a translate, so the entrance
     animation below owns `transform` outright and the two cannot fight. */
  inset: 0;
  margin: auto;
  width: min(1060px, 94vw);
  height: min(760px, 92vh);
  z-index: 7900;

  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px 16px;

  font-family: 'Kalam', cursive;
  color: #3a2a10;

  /* The same aged parchment as the form's tag: a lit corner, a stained one, and
     the desk's own sepia dot grid. */
  background-color: #f3e7c9;
  background-image:
    radial-gradient(ellipse 60% 44% at 22% 8%, rgba(255, 251, 238, 0.9) 0%, transparent 70%),
    radial-gradient(ellipse 54% 46% at 92% 96%, rgba(146, 108, 50, 0.20) 0%, transparent 72%),
    radial-gradient(circle, rgba(120, 90, 55, 0.17) 1.1px, transparent 1.7px);
  background-size: auto, auto, 18px 18px;
  border: 1px solid #9a7434;
  border-radius: 3px;
  box-shadow:
    inset 0 0 0 1px rgba(255, 252, 240, 0.55),
    inset 0 0 40px rgba(120, 86, 34, 0.16),
    0 26px 64px rgba(20, 12, 3, 0.6);
  animation: st-rise 0.3s cubic-bezier(0.2, 0.88, 0.3, 1) both;
}
.st-panel:focus { outline: none; }

/* Both entrances are transform/opacity only, and both replay by themselves: an
   animation restarts whenever an element goes from display:none to shown, which
   is exactly what opening the studio does. */
@keyframes st-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes st-rise {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* The other person's cursor, over the canvas. Task 14 fills it; it is here now
   so the rung exists and can be measured with the other two. Never a click
   target — a cursor that swallowed a pointer event would stop the person it is
   drawn for from drawing. */
.st-cursors {
  position: fixed;
  inset: 0;
  z-index: 7950;
  pointer-events: none;
}

/* ---------- The header ---------- */
.st-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}
.st-title {
  margin: 0;
  font-family: 'Caveat', cursive;
  font-weight: 600;
  font-size: 26px;
  line-height: 1.1;
  color: #4a3520;
}
.st-name-label {
  font-family: 'Caveat', cursive;
  font-size: 19px;
  color: #6b4e22;
  margin-left: 6px;
}
#st-name {
  width: 190px;
  padding: 5px 9px;
  font: inherit;
  font-size: 15px;
  color: #33240c;
  background: rgba(255, 253, 245, 0.62);
  border: 1px solid #a98a4e;
  border-radius: 2px;
  /* Ruled-paper seat rather than a floating input box, matching the form. */
  box-shadow: inset 0 1px 3px rgba(120, 86, 34, 0.18);
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
#st-name:focus {
  outline: none;
  border-color: #8a6520;
  box-shadow: inset 0 1px 3px rgba(120, 86, 34, 0.18), 0 0 0 2px rgba(255, 214, 122, 0.55);
}
#st-name:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  background: rgba(214, 198, 166, 0.5);
}
.st-spacer { flex: 1; }

.st-head button {
  padding: 7px 15px;
  font: inherit;
  font-size: 13px;
  color: #5c421a;
  background: rgba(255, 252, 242, 0.5);
  border: 1px solid rgba(138, 101, 32, 0.4);
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5);
  cursor: pointer;
  transition: transform 0.16s ease, box-shadow 0.16s ease, opacity 0.16s ease;
}
.st-head button:hover:not(:disabled) { transform: translateY(-1px); }
.st-head button:focus-visible { outline: 2px solid #ffd77a; outline-offset: 2px; }
.st-head button:disabled { opacity: 0.45; cursor: not-allowed; }

/* "Save as new" — the variant. Plain, deliberately: there is only ever one
   primary in a room, and this is the rarer of the two verbs. `nowrap` because
   its label is three words in a header that is already a flex row with a text
   input in it. */
.st-head .st-secondary { white-space: nowrap; }

/* Save is the only brass in the room — the same plate as the trunk's own, and
   the same plate the form's Save wears. */
.st-head .st-primary {
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #2b1d06;
  text-shadow: 0 1px 0 rgba(255, 246, 214, 0.5);
  border-color: rgba(0, 0, 0, 0.42);
  background-image: linear-gradient(157deg, #d9ab52 0%, #8a6520 26%, #d9ab52 52%, #8a6520 74%, #4e380e 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -1px 1px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(0, 0, 0, 0.45);
}
.st-head .st-primary:hover:not(:disabled) {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    inset 0 -1px 1px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(0, 0, 0, 0.45),
    0 0 13px rgba(255, 214, 122, 0.6);
}

/* ---------- The studio has lost the server ----------
   The same line, in the same warm ink, as the form's .tf-offline. A studio that
   goes quiet without saying so is indistinguishable from a broken one. */
.st-offline {
  flex: 0 0 auto;
  margin: -4px 0 0;
  font-family: 'Caveat', cursive;
  font-size: 17px;
  line-height: 1.15;
  color: #8a4b2a;
  opacity: 0.92;
}

/* ---------- The tool strip ----------
   One horizontal strip across the top, the rest canvas, grouped exactly as the
   design's §4 sketch draws it:

       ✒ 🖌 ▭ ◯ ╱ T ⌫ ↶ │ ▪▪▪▪▪▪ + │ ─────●─── │ start from ▾

   i.e. the tools, then the inks and a free colour picker, then the size, then
   "start from" — with a `│` between the groups. studio.js builds the controls;
   this is what they are made of. Everything in it is a real <button> or
   <input>, which is not a style choice: `disabled` does nothing at all on
   anything else, so a tool built from a div would stay live with the socket
   down. See buildTools() in studio.js. */
.st-tools {
  flex: 0 0 auto;
  min-height: 38px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px 6px;
  border-top: 1px dashed rgba(138, 101, 32, 0.45);
  border-bottom: 1px dashed rgba(138, 101, 32, 0.45);
}

/* A group is one `│`-separated cluster from the sketch. It is a real element
   rather than spacing because Tasks 11-13 open and close whole groups (shape
   options belong to the shape tools, a font picker to text) — and studio.js's
   focus trap already walks ancestors for `hidden` so a control in a shut group
   leaves the Tab cycle with it.

   THE `display: flex` BELOW IS THE SECOND HALF OF THE TRAP the rule at the top
   of this file exists for, and it is the one that sprang: it beat the UA's
   `[hidden] { display: none }` on origin, so shutting a group did nothing at
   all and the fill toggle stayed on screen — and clickable — for the pen. Task
   11's shape options are the first group that is ever shut. Nothing is needed
   HERE now: `#studio-panel [hidden]` is (1,1,0) against this block's (0,1,0)
   and closes every group, including the ones Tasks 12-13 add. */
.st-group {
  display: flex;
  align-items: center;
  gap: 4px;
}

.st-sep {
  width: 1px;
  align-self: stretch;
  margin: 2px 4px;
  background: linear-gradient(rgba(138, 101, 32, 0), rgba(138, 101, 32, 0.5), rgba(138, 101, 32, 0));
}

/* A tool is a stamp on the workbench: pressed in, not floating. */
.st-tool {
  width: 30px;
  height: 28px;
  padding: 0;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  color: #4a3520;
  background: rgba(255, 252, 242, 0.5);
  border: 1px solid rgba(138, 101, 32, 0.4);
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5);
  cursor: pointer;
  transition: box-shadow 0.16s ease, opacity 0.16s ease;
}
.st-tool.on {
  background: rgba(217, 171, 82, 0.42);
  border-color: #8a6520;
  box-shadow: inset 0 1px 3px rgba(78, 56, 14, 0.42);
}
.st-tool:focus-visible,
.st-swatch:focus-visible,
.st-ink-free:focus-visible,
.st-size:focus-visible { outline: 2px solid #ffd77a; outline-offset: 2px; }

/* The inks. The swatch IS its colour, so it carries no label — the accessible
   name is on the button and the title shows on hover. */
.st-swatch {
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid rgba(43, 29, 6, 0.55);
  border-radius: 50%;
  cursor: pointer;
  transition: box-shadow 0.16s ease, opacity 0.16s ease;
}
.st-swatch.on {
  box-shadow: 0 0 0 2px #f3e7c9, 0 0 0 3px #8a6520;
}
.st-ink-free {
  width: 26px;
  height: 22px;
  padding: 0 1px;
  background: rgba(255, 252, 242, 0.5);
  border: 1px solid rgba(138, 101, 32, 0.5);
  border-radius: 2px;
  cursor: pointer;
}

.st-size-label,
.st-fill-label,
.st-size-out {
  font-family: 'Caveat', cursive;
  font-size: 16px;
  color: #6b4e22;
}

/* ---------- The shape options ----------
   Shown only while one of the three shape tools is chosen, so pen, brush and
   text see exactly the strip the design's §4 sketch draws. The `│` that
   separates it from the tools is INSIDE the group, so shutting the group takes
   the rule with it rather than leaving two side by side.

   The fill toggle wears .st-tool as well, because it is one of the same family
   of stamps and `.on` already draws a pressed one. Its glyph carries the state
   too — ◻ hollow, ◼ solid, one Unicode block, the same square either way — so
   "painted inside the outline" is legible without reading the pressed state off
   a background colour. */
.st-fill {
  /* Wider than a tool button by nothing at all; it is the same stamp. The only
     thing this block owns is the glyph's optical size, which is a geometric
     shape rather than an emoji and sits small at the tools' 15px. */
  font-size: 17px;
}
.st-size-out {
  /* Fixed so the strip does not shuffle sideways as the number changes width. */
  min-width: 24px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.st-size {
  width: 120px;
  accent-color: #8a6520;
  cursor: pointer;
}

/* ---------- The lettering's options ----------
   Shown only while the text tool is chosen, and the stroke-weight slider is
   shut in the same movement — glyph outlines are FILLED shapes with no stroke
   at all, so a weight control beside them would change nothing. studio.js's
   reflectTools() owns both, and the `hidden` that does the shutting is the one
   rule at the top of this file. */
.st-face-pick,
.st-preset {
  font: inherit;
  font-size: 14px;
  padding: 2px 4px;
  color: #4a3520;
  background: rgba(255, 252, 242, 0.5);
  border: 1px solid rgba(138, 101, 32, 0.4);
  border-radius: 2px;
  cursor: pointer;
}
.st-face-pick:focus-visible,
.st-preset:focus-visible { outline: 2px solid #ffd77a; outline-offset: 2px; }

/* ---------- start from a preset ----------
   The last group in the design's sketch, and the only <select> in the strip
   that is open in every state — the face picker beside it belongs to the text
   tool and shuts with it.

   IT IS SIZED BY ITS OWN CONTENT and given no width here on purpose: the
   longest label is "Hogwarts Castle" and the strip is `flex-wrap: wrap`, so a
   fixed width would either clip a crest's name or reserve space for one that
   is not shown. What this block owns is the one thing the shared box above
   cannot know: this control names an action rather than a setting, so its
   resting label is the quieter of the two weights. */
.st-preset { font-style: italic; }
.st-preset option { font-style: normal; }

/* ---------- Typing, on the canvas ----------
   The inline editor: a real <input>, placed by studio.js at the point that was
   clicked, in the face and at the size the outline will be committed at. It is
   a guide and not the artefact — what saves is the glyph outline opentype.js
   generates, and the actual-size preview in the corner is where you see the
   truth of it.

   NO BACKGROUND AND NO BORDER, so the letters sit on the drawing rather than in
   a box on top of it; the caret and the placeholder-less emptiness are the only
   things that say a control is there. Padding is zero and the line height is
   written by studio.js from the face's own ascent-to-descent, because those two
   are what decide where the browser puts the baseline — and it has to be the
   baseline opentype will use, or the letters jump when Enter is pressed.

   Its colour is the current ink, also written by studio.js: one more thing that
   would be a lie if it were stated here as well. */
.st-text-input {
  position: absolute;
  z-index: 1;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  outline: none;
  /* A drawing surface underneath, so the box must not carry the crosshair or
     the canvas's own selection ban into the one place typing is expected. */
  cursor: text;
  user-select: text;
  -webkit-user-select: text;
}

/* A refused stroke says so here. Warm ink rather than an alarm: running a
   drawing out of room is an ordinary thing to do, not a fault. */
.st-note {
  font-family: 'Caveat', cursive;
  font-size: 16px;
  line-height: 1.1;
  color: #8a4b2a;
}

/* ---------- The stage, and the square inside it ----------
   The letterbox. Darker than the drawing surface so the boundary of what can
   ever be shown is not a matter of opinion: an emblem is square, and a stroke
   outside the square is a stroke nobody will ever see on a trunk. */
.st-stage {
  position: relative;
  flex: 1 1 auto;
  /* A flex item's default min-height is its content, which would let the stage
     grow past the panel instead of taking the space that is left. */
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  background: #4b3a1d;
  background-image:
    radial-gradient(ellipse 70% 60% at 50% 40%, rgba(0, 0, 0, 0.22) 0%, transparent 76%);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35), inset 0 2px 14px rgba(0, 0, 0, 0.45);
}

/* Paper, not parchment: a drawn emblem is full colour and keeps every colour it
   was drawn in, so it needs a surface that does not tint it. Its width and
   height are written by studio.js — see the note at the top of this file. */
.st-canvas {
  position: relative;
  flex: 0 0 auto;
  background: #fdfaf2;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.4),
    0 0 0 3px rgba(243, 231, 201, 0.14),
    0 6px 22px rgba(0, 0, 0, 0.45);
  cursor: crosshair;
  /* The drawing surface is a drawing surface: a drag across it must not select
     text or start the browser's own image drag. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
.st-canvas svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- The stroke being drawn right now ----------
   Its own layer, over the square, holding exactly one stroke.

   NOT A CHILD OF .st-canvas, because studio.js owns that element's innerHTML
   outright and rewrites it on every repaint — a child would be destroyed by the
   next committed stroke. It is a sibling inside .st-stage instead, given the
   same side length by layout() and centred by `inset: 0; margin: auto`, which
   lands in exactly the same place as the flex centring the square gets because
   .st-stage has neither padding nor border.

   The alternative was repainting the whole canvas, and the preview trunk with
   it, on every pointermove. This costs one stroke's worth of markup per move
   however much is already on the canvas.

   pointer-events: none, AND IT IS LOAD-BEARING RATHER THAN TIDY. This layer is
   given exactly the square's box, and it is a SIBLING of .st-canvas rather than
   a child — so without it the layer is the hit target and the pointerdown
   bubbles to .st-stage, never reaching the canvas that listens for it. No drag
   would ever start and the studio could not be drawn on at all. It is the DOWN
   that is swallowed, not the moves: once a drag has started the canvas has
   captured the pointer and every later event is routed to it regardless of what
   is on top. */
.st-live {
  position: absolute;
  inset: 0;
  margin: auto;
  pointer-events: none;
}
.st-live svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- The actual-size preview ----------
   Absolutely positioned inside .st-stage, the NEAREST positioned ancestor it
   has (.st-panel is `position: fixed` and is one too, further up), so its
   offsets are the stage's corner — and it is out of the flow, but it DOES take
   a share of the room the
   square gets. It stands in the letterbox at the bottom-right and the square is
   centred, so a square fitted to the whole stage would slide underneath it and
   the bottom-right corner of the drawing would be under an opaque shelf you
   cannot see through (`pointer-events: none` means you could still draw there,
   blind, which is worse than not being able to). studio.js therefore reserves a
   gutter: it reads THESE TWO VALUES back off this element and keeps the square
   out of `2 * (width + right)` of stage — doubled because centring gives the
   left side whatever the right side takes. See layout() in studio.js.

   That is why the width and the right offset are written HERE and nowhere else,
   including in the 640px block below: studio.js measures rather than copies, so
   the media query moves the gutter with the preview and no number has a second
   home to drift from. Both are px on purpose: layout() refuses to reserve a
   gutter from a length it cannot resolve to px, so a percentage width here
   would quietly stop the reserving rather than mis-size it.

   When the stage is too narrow for the gutter to leave a surface at least as
   wide as this preview, studio.js sets `hidden` on this element and takes the
   gutter back — and that keeps working if a `display` is ever added to this
   block, because `#studio-panel [hidden]` at the top of this file outranks
   anything a class can declare. It matters more here than anywhere: layout()
   takes the whole width back when it hides the preview, so a `hidden` that
   stopped hiding would paint the preview at full size over a square that has
   just been widened to run underneath it.

   What is inside it is a REAL trunk at desk size — studio.js renders it through
   Trunks.renderTrunk — so the emblem on its face is exactly the size the emblem
   will be on the desk. If the drawing turns to mush there, it turns to mush
   there. */
.st-face {
  position: absolute;
  right: 22px;
  bottom: 20px;
  width: 200px;
  height: 138px;
  border-radius: 3px;
  /* A shallow shelf: warm light above, a contact pool below, so the trunk is
     standing on something rather than floating over the canvas. */
  background-image:
    radial-gradient(ellipse 62% 24% at 50% 88%, rgba(0, 0, 0, 0.34) 0%, transparent 72%),
    radial-gradient(ellipse 76% 66% at 50% 34%, rgba(255, 247, 227, 0.14) 0%, transparent 74%);
  overflow: hidden;
  pointer-events: none;
}

.st-face .trunk {
  /* No --u, --tw or --th override anywhere in this block, and that is the whole
     point of the preview: the trunk keeps the size a desk trunk has, including
     under the 640px media query in trunks.css that shrinks every trunk at once.
     Only its placement is ours. */
  left: 50%;
  top: 50%;
  margin-left: calc(var(--tw) / -2);
  margin-top: calc(var(--th) / -2 - 4px);
  /* A picture, not a control. Without this the trunk's own click handler would
     open the preview's lid and broadcast a lid toggle for an id that exists
     nowhere but this panel. */
  pointer-events: none;
  cursor: default;
}

/* ---------- Read-only, and the socket being down ----------
   Cancel stays live. A frozen modal with no way out is the one state worse than
   the bug the freeze fixes: this panel covers the page. */
.st-panel.readonly input,
.st-panel.readonly button:not(#st-cancel) {
  pointer-events: none;
  opacity: 0.55;
}

/* AND ANYTHING ELSE IN THE STRIP THAT CAN BE REACHED, because `disabled` is
   INERT on anything that is not a form control and studio.js's applyEnabled
   writes it onto everything the strip holds. Measured on this branch: with the
   socket down, a div[tabindex="0"] in this strip took `disabled` as a plain JS
   expando, computed pointer-events:auto and opacity:1, and ITS CLICK HANDLER
   FIRED — while a real <button> beside it computed pointer-events:none /
   opacity:0.55 and did not. The keyboard was never at risk (the expando is
   enough for focusables() to skip it); the pointer was.

   Every tool studio.js builds today is a real <button> or <input> and is
   already covered by the two selectors above, which is the actual fix. This is
   the backstop for the next widget that is neither — the design's own
   "start from ▾" disclosure is one — so that the hole cannot reopen quietly. */
.st-panel.readonly .st-tools [tabindex] {
  pointer-events: none;
  opacity: 0.55;
}

/* THE SURFACE, AND IT IS A NARROWER STATE THAN THE THREE RULES ABOVE. Those are
   about the STRIP, and both freezes earn them: a tool, an ink and a width are
   shared state that cannot be negotiated with a server that is not there or by a
   viewer who is only watching. Drawing is the thing the two part company over —
   a studio whose socket has died still takes strokes and holds them until it
   comes back (studio.js, `held`) — so the barred cursor is written for the
   watcher alone, under its own class. A "no" cursor over lines that are being
   kept would be a lie about them, in the one place the hand is looking. */
.st-panel.watching .st-canvas { cursor: not-allowed; }

/* ---------- Small screens ----------
   Spec §8: the studio is desktop-only, and the refusal lives in two places that
   are not the same one — main.js's requestStudio, which will not ask for a
   canvas at this size, and trunks.css, which hides the two tiles that ask.
   This block is only so a studio already open when a desktop window is dragged
   narrow stays usable rather than spilling off the page: readOnly follows the
   crossing (main.js's matchMedia listener), so what is left is the geometry. */
@media (max-width: 640px) {
  .st-panel { width: 96vw; height: 94vh; padding: 10px; }
  /* The gutter studio.js reserves for the preview shrinks with it, because
     layout() reads this element's used width and right offset rather than
     holding a copy of them. Nothing in studio.js knows this query exists. */
  .st-face { right: 10px; bottom: 10px; width: 140px; height: 100px; }
  #st-name { width: 120px; }
}

/* ---------- Reduced motion ----------
   The studio arrives rather than settling. Nothing else in here moves: the
   preview trunk is deliberately never given `.hot`, so it has no personality
   animation to still, and trunks.css's own blanket covers it regardless. */
@media (prefers-reduced-motion: reduce) {
  .st-backdrop,
  .st-panel { animation: none; }
  .st-panel button,
  .st-panel input { transition: none; }
  .st-head button:hover:not(:disabled) { transform: none; }
}
