/* Fabulous — pins: a note stuck to a place on the solid.
 *
 * THE SAME TWO RULES AS `app.css`, and this file is not allowed a third one.
 * The viewer is the hero, so a pin is a whisper over it — a hairline dot and
 * a small dark card, in the same idiom as `#layers` and `#step`: no panel, no
 * title, no shadow theatre. And the palette is the Faber system's, taken from
 * `app.css`'s own `:root` rather than restated here, because a second copy of
 * `--ink-rgb` is a second palette the day one of them is edited.
 *
 * THE ACID AND NOT BRASS, deliberately. The brief asked for "the brass accent
 * used sparingly"; there is no brass left in this product — `dev/gate.py`
 * fails on its hex ANYWHERE on the screen, and `ui/CLAUDE.md` records why
 * (Fabulous shipped a brass accent and read as a different company's product
 * beside crazyshit and the fab desktop app). That check reads VALUES, which is
 * why this comment cannot quote the one it is explaining — writing the hex
 * down to say it is banned is the thing that fails the check.
 *
 * The one accent is the site's ACID, and it is spent on decisions: here that
 * is the pin the user PUT there, which is a decision they made about the
 * geometry.
 * The agent's own pins get ink.
 *
 * EVERY NAME IN THIS FILE IS `pins-` OR `#pins-…`, so nothing here can collide
 * with `app.css` and nothing in `app.css` can reach in.
 */

/* Above the canvas, BELOW the veil (5) and below the wordmark, the quota, the
   layers, the STEP and the project pastille (8 and 9). Two things fall out of
   that and both are wanted: a staged receipt over a finished model dims the
   pins with it (the veil paints its own ground at `rgb(var(--ground-rgb) / 0.66)`), and
   the projects overlay (14) covers them completely. */
#pins-layer {
  position: fixed;
  inset: 0;
  z-index: 4;
  /* The LAYER never takes a click — only the bubbles inside it do. Otherwise
     a transparent rectangle over the whole viewport would eat every orbit. */
  pointer-events: none;
}

/* THE CROSSHAIR IS THE MODE, and it is the only thing that says so from
   inside the viewer. Placing is off by default; while it is on, the pointer
   over the geometry stops being a hand that turns the part. */
body.pins-armed #stage canvas { cursor: crosshair; }

/* A zero-sized box AT the anchor, with the dot and the note hung off it. The
   obvious flex row does not work: the note has to be able to open to the LEFT
   near the right edge of the window, and `flex-direction: row-reverse` moves
   the DOT as well — which puts the mark somewhere the user never clicked. */
.pins-pin {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  will-change: transform;
}

/* Behind the camera, or projected somewhere off the window. Not `opacity: 0`:
   a bubble that is invisible and still laid out is a bubble that still eats
   the click somebody aimed at the part underneath it. */
.pins-pin.pins-off,
.pins-mark.pins-off,
#pins-spot.pins-off { display: none; }

/* ------------------------------------------- what Ctrl is holding open
 *
 * A ring at each place collected so far, before there is a bubble to hang
 * them on. The lit faces in the scene are most of the answer; this is what
 * makes the set COUNTABLE — a face seen edge-on lights as a line, an edge
 * target is a line either way, and a bare point on a dropped mesh has nothing
 * in the scene to light at all. Selection strength, because a collection is a
 * decision the user is making and not something the pointer is passing over.
 */
.pins-mark {
  position: absolute;
  top: 0;
  left: 0;
  width: 13px;
  height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border-radius: var(--radius);
  border: 1px solid var(--accent-deep);
  background: rgb(var(--accent-rgb) / 0.16);
  pointer-events: none;
  will-change: transform;
}

/* --------------------------------------------------------------- the mark */

.pins-dot {
  position: absolute;
  left: -9px;
  top: -9px;
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  border-radius: var(--radius);
  border: 1px solid rgb(var(--accent-rgb) / 0.5);
  background: rgb(var(--ground-rgb) / 0.72);
  backdrop-filter: blur(6px);
  color: var(--accent-deep);
  font-family: var(--mono);
  font-size: 9.5px;
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  pointer-events: auto;
  cursor: pointer;
  transition: border-color var(--wake), background var(--wake);
}

.pins-dot:hover { border-color: var(--accent-deep); background: rgb(var(--ground-rgb) / 0.9); }

/* --------------------------------------------------------------- the note */

.pins-note {
  position: absolute;
  left: 16px;
  top: -11px;
  width: max-content;
  max-width: 208px;
  padding: 7px 22px 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgb(var(--ground-rgb) / 0.66);
  backdrop-filter: blur(6px);
  pointer-events: auto;
  cursor: text;
  transition: border-color var(--wake), background var(--wake);
}

.pins-note:hover { border-color: rgb(var(--ink-rgb) / 0.2); }

/* Near the right edge the note opens the other way. `right` is measured from
   this box's right edge, and the box is zero wide — so it is the anchor. */
.pins-flip .pins-note { left: auto; right: 16px; }

.pins-text {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-soft);
  /* A note is one or two lines of somebody's own words, newlines and all. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* THE WAY OUT, AND IT HAS TO BE VISIBLE. A pin somebody cannot remove is a
   mark on their part they did not agree to keep, so this is not a hover-only
   affordance — it is there at a third, and full strength under the pointer. */
.pins-x {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  border-radius: var(--radius);
  background: transparent;
  color: rgb(var(--ink-rgb) / 0.32);
  font-family: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: color var(--wake), background var(--wake);
}

.pins-x:hover { color: var(--ink); background: rgb(var(--ink-rgb) / 0.1); }
.pins-x:focus-visible { outline: none; color: var(--ink); background: rgb(var(--ink-rgb) / 0.14); }

/* --------------------------------------------------------------- writing it */

.pins-editing .pins-note {
  border-color: rgb(var(--accent-rgb) / 0.42);
  background: rgb(var(--ground-rgb) / 0.86);
  padding-right: 10px;
}

.pins-edit {
  display: block;
  width: 184px;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  line-height: 1.45;
  resize: none;
  overflow: hidden;
  outline: none;
}

.pins-edit::placeholder { color: rgb(var(--ink-rgb) / 0.26); }

/* ------------------------------------------------- which one is lit, on screen
 *
 * The face itself lights up in the SCENE — `pins.js` hangs a draw-ranged
 * overlay on the leaf's own geometry — and this is that state read back onto
 * the bubble, so the two halves of "which one is this note about" agree. It is
 * the acid at full strength, which the rest of this file never uses: this is
 * the one pin the user is looking at.
 */
.pins-lit .pins-dot {
  border-color: var(--accent-deep);
  background: rgb(var(--ground-rgb) / 0.92);
  box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / 0.14);
}

.pins-lit .pins-note { border-color: rgb(var(--accent-rgb) / 0.34); }

/* A `kind: "point"` PIN HAS NO FACE TO LIGHT, and inventing one would be the
   confident wrong answer this whole feature exists to stop giving. The ring is
   the marking, and it is the whole of it. */
.pins-lit.pins-point .pins-dot { box-shadow: 0 0 0 5px rgb(var(--accent-rgb) / 0.1); }

/* ONE NOTE ABOUT SEVERAL PLACES, said on the dot itself: a second ring
   outside the first, which is the same mark twice and reads as one number
   standing for more than one thing. It is drawn unlit as well as lit, because
   the question "is this note about one face or four" is asked of a bubble
   nobody is hovering — that is exactly when the geometry says nothing. */
.pins-many .pins-dot { box-shadow: 0 0 0 2px rgb(var(--ground-rgb) / 0.72),
                                   0 0 0 3px rgb(var(--accent-rgb) / 0.5); }
.pins-lit.pins-many .pins-dot { box-shadow: 0 0 0 2px rgb(var(--ground-rgb) / 0.92),
                                            0 0 0 4px rgb(var(--accent-rgb) / 0.3); }

/* THE SAME PROBLEM ON THE WHOLE OF MESH2CAD. A dropped mesh has no faces —
   `mesh_view._part` ships `triangles_per_face: []`, because a facet is not a
   face — so every hover over one resolves to a bare point and there is nothing
   in the scene to light. This is what the pointer gets instead: a ring at the
   millimetres under it, projected by the same code that follows the bubbles.
   Hover strength, never selection strength. */
#pins-spot {
  position: absolute;
  top: 0;
  left: 0;
  width: 13px;
  height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border-radius: var(--radius);
  border: 1px solid rgb(var(--accent-rgb) / 0.55);
  pointer-events: none;
  will-change: transform;
}

/* --------------------------------------------------- a pin behind the solid
 *
 * A note anchored to the far side of the part reads, without this, as a note
 * about the near side — which on a bracket with a hole through it is the
 * difference between two faces 8 mm apart. Dimmed rather than hidden: it is
 * still the user's note and it is still there, and hiding it would make an
 * orbit look like it deleted something. */
.pins-behind { opacity: 0.34; }
.pins-behind .pins-dot { background: transparent; border-style: dashed; }

/* Somebody who asked for less motion still gets every pin, at once. */
@media (prefers-reduced-motion: reduce) {
  .pins-dot, .pins-note, .pins-x { transition: none; }
}
