/* =========================================================
   items.css — the item box, its selection ring, and the generic card plate.
   FIRST OF SIX. The load order IS the cascade:
     items > ghosts > fences > media > cards > item-chrome
   Pairs with canvas/items.ts and canvas/renderers.ts.
   ========================================================= */

.item {
  position: absolute;
  background: var(--item-bg);
  /* The corner the item actually draws: --shape-radius is per-type (notes
     override it), and --own-radius caps it at half the shorter side so
     nothing ends up tracing a curve the browser quietly flattened. */
  --shape-radius: var(--radius);
  --own-radius: min(var(--shape-radius), var(--half-min, 9999px));
  --handle-w: 30px;
  border-radius: var(--own-radius);
  box-shadow: none;
  /* Not clipped — grips sit outside. .item-body clips instead. */
  overflow: visible;
  /* Default, not grab — the hand appears only when selected. */
  cursor: default;
  user-select: none;
  -webkit-user-select: none;
  /* Ring geometry collapsed flat. Expanded by .item.is-selected below. */
  --sel-edge: 0px;
  --mark-radius: var(--own-radius);
  --mark-size: 0px;
  /* Lift and tilt are zero at the plain end of the whimsy axis. The resting
     tilt is each item's own factor times the axis setting; drag tilt adds
     on top so a crooked item stays crooked when picked up. */
  translate: 0 calc(var(--lift) * var(--iz));
  rotate: calc(var(--item-tilt, 0) * var(--tilt-max) + var(--tilt));
  scale: var(--grow);
  --lift: 0px;
  --tilt: 0deg;
  --grow: 1;
  /* opacity rides here rather than separately — `transition` is a shorthand,
     so a second declaration replaces the whole list. tokens.css repeats this
     list's order for :root.is-fading, so keep the two in step. */
  transition: box-shadow var(--dur-base) var(--ease),
              translate var(--dur-base) var(--ease-back),
              rotate var(--dur-base) var(--ease-back),
              scale var(--dur-base) var(--ease-back),
              opacity var(--dur-base) var(--ease);
}
/* Snapped to grid: no tilt. `rotate` outright rather than zeroing --tilt-max,
   because the tokens can be written inline on :root and no selector can
   outrank that. Still inherits the transition, so the board straightens. */
:root[data-snap] .item { rotate: 0deg; }
/* Tilt cards off (Look tab): the resting lean goes, and the lean a drag adds
   stays, since that one is the card in the hand. */
:root[data-no-tilt] .item { rotate: var(--tilt); }

/* The ring is the pointer indicator; the lift is separate because .is-hover
   covers the hovered card AND its pinned stickies. */
.item:hover {
  box-shadow: 0 0 0 var(--board-hairline) var(--rule-2);
}
/* The hover lift is divided by --iz to cancel the one in `translate`, so it is
   in board units and scales with the card, as the grow does: the pop looks the
   same at every zoom as it does at 100%. In screen pixels it was a fixed jump
   that threw a card at far zoom by most of its own height. The drag lift stays
   in screen pixels, because that card sits under the pointer. */
.item.is-hover {
  --lift: calc(var(--lift-hover) / var(--iz));
  --grow: var(--grow-hover);
}
/* Selected: now the grab hand appears. */
.item.is-selected { cursor: grab; }
.item.is-dragging {
  cursor: grabbing;
  --lift: var(--lift-drag);
  --tilt: var(--tilt-drag);
  --grow: var(--grow-hover);
}
/* A selected card holds still under the pointer. The hover lift moves the
   grips (children), making them impossible to hit at the soft whimsy tier
   where --lift-hover is 6px and --grow-hover is 1.03. Not while dragging —
   picking up should still lift and tilt. The resting tilt is left alone
   because straightening cards on selection would be louder than the problem. */
.item.is-selected:not(.is-dragging).is-hover {
  --lift: 0px;
  --grow: 1;
}

/* The pin glyph, drawn into the pin badge (.item-pin, item-chrome.css) as a
   mask so it takes the badge's colour. */
.item-pin::before {
  --pin-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 2h6M6.5 2v4.2c0 .9-.6 1.4-1.3 1.9-.6.4-1 .8-1 1.4h7.6c0-.6-.4-1-1-1.4-.7-.5-1.3-1-1.3-1.9V2M8 9.5V14'/%3E%3C/svg%3E");
  content: '';
  width: 62%;
  height: 62%;
  background: currentColor;
  -webkit-mask: var(--pin-glyph) center / contain no-repeat;
  mask: var(--pin-glyph) center / contain no-repeat;
}
/* Selection ring on a pseudo-element rather than outline, because engines
   disagree about whether outline-offset grows the corner radius. The box's
   outer edge is --sel-edge out from the item; the border grows inward.
   Delays let the ring expand instantly on selection and collapse unseen. */
.item::before {
  content: '';
  position: absolute;
  inset: calc(-1 * var(--sel-edge));
  border: 0 solid var(--select);
  border-radius: var(--mark-radius);
  pointer-events: none;
  transition: border-width var(--dur-fast) var(--ease),
              inset 0s var(--dur-fast),
              border-radius 0s var(--dur-fast);
}
.item.is-selected::before {
  border-width: calc(var(--sel-line) * var(--iz));
  transition: border-width var(--dur-fast) var(--ease);
}

/* Locked cards have no ring — the ring outlines a grab that cannot happen.
   The padlock (item-chrome.css) says why instead. */
.item[data-locked].is-selected::before { border-width: 0; }

/* Ring geometry in two versions: collapsed (on .item) and expanded (here).
   The split is a performance one — unselected cards were re-deriving three
   --iz values they do not use, sixty times a second, for every card on
   screen. R+d parallels rounded corners; at radius 0 the growth saturates
   to 0 so a square corner stays square. */
.item.is-selected,
.item.is-stick-target {
  --sel-edge: calc((var(--sel-gap) + var(--sel-line)) * var(--iz));
  --mark-radius: min(calc(var(--own-radius) + var(--sel-edge)),
                     calc(var(--own-radius) * 1000));
  --mark-size: calc(var(--mark-radius) + var(--sel-reach) * var(--iz));
}

/* Stick target: same ring as selection, nothing else (no grips, no menu). */
.item.is-stick-target::before {
  border-width: calc(var(--sel-line) * var(--iz));
  transition: border-width var(--dur-fast) var(--ease);
}

.item-body {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}
.item-body img,
.item-body video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.item[data-fit="contain"] .item-body img,
.item[data-fit="contain"] .item-body video { object-fit: contain; }

/* The letterbox round a picture fitted into grid cells: a blurred, dimmed copy
   of it where the dial allows blur (renderers.ts picWell), the card's paper
   where it does not. Sized to the photo window, so a Softish print's mat stays
   paper - --mat and --chin are unset everywhere else. The haze overhangs the
   well so the blur has no pale rim, and the well clips it. */
.pic-well { display: none; }
:root[data-q-blur="on"] .item[data-fit="contain"]:not([data-bare]) .pic-well {
  display: block;
  position: absolute;
  top: var(--mat, 0px);
  left: var(--mat, 0px);
  right: var(--mat, 0px);
  bottom: var(--chin, 0px);
  overflow: hidden;
}
.pic-well > span {
  position: absolute;
  inset: -15%;
  background: var(--pic-bg, none) center / cover no-repeat;
  filter: blur(calc(var(--half-min, 120px) * 0.12)) brightness(0.72) saturate(1.1);
}
/* In front of the well, which is positioned and would otherwise paint over it. */
:root[data-q-blur="on"] .item[data-fit="contain"] .item-body :is(img, video) { position: relative; }

/* Picture adjustments as one filter property. `none` default avoids
   promoting every unadjusted card to its own compositor layer. */
.item-body { filter: var(--item-filter, none); }

/* Mirror. Safe on .item-body because it is inset:0 — mirroring about its
   centre lands on the card. The caption (.item-bar) is a sibling, so
   flipping a photo does not reverse its label. */
.item-body { transform: var(--item-flip, none); }

/* Zoomed-out stand-in (frozen GIF / thumbnail). is-ready guards against
   a blank swap while the bytes are still decoding. */
.item-body img.still { display: none; }
#world.is-stilled .item-body img.still.is-ready { display: block; }
#world.is-stilled .item-body img:not(.still):has(+ .still.is-ready) { display: none; }
/* Animated GIFs off (the quality dial, stills.ts): the same swap for GIFs
   only, at every zoom. Photographs keep their display copy until the rung. */
#world.is-held .item-body img[data-gif] + img.still.is-ready { display: block; }
#world.is-held .item-body img[data-gif]:has(+ .still.is-ready) { display: none; }

/* Video twin: swaps to the still only while the clip is parked — a playing
   clip must keep its picture. */
#world.is-stilled .item-body:has(> .vplayer.is-playing) img.still { display: none; }
#world.is-stilled .item-body:not(:has(> .vplayer.is-playing)) video:has(+ .still.is-ready) {
  display: none;
}

/* The generic card plate. Ruled just inside the edge (bookplate ruling). */
.card {
  position: relative;
  width: 100%;
  height: 100%;
  /* Named so the audio sleeve's absolute positioning can read the same value. */
  --card-pad-y: 15px;
  --card-pad-x: 16px;
  padding: var(--card-pad-y) var(--card-pad-x);
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-radius: inherit;
  background: var(--paper-card);
  /* Inset shadow instead of border — a real border is shaved by .item's clip. */
  box-shadow: inset 0 0 0 var(--board-hairline) var(--item-border);
  overflow: hidden;
}

/* Inner bookplate rule. The gap is in world units (scales with the card);
   the width is --board-hairline (constant on screen). Both sides of the
   radius subtraction are world units, so the corners stay concentric. */
.card::after {
  content: '';
  position: absolute;
  inset: var(--card-rule-gap);
  border: var(--board-hairline) solid var(--rule);
  border-radius: max(0px, calc(var(--own-radius) - var(--card-rule-gap)));
  pointer-events: none;
}
.card-icon {
  font-family: var(--font-display);
  /* World-space type — zooms with the board. Not on the chrome scale.
     Sorted deliberately off-scale by visual-audit item 2. */
  font-size: 11px;
  font-style: var(--display-italic);
  letter-spacing: calc(0.16em * var(--ls-scale));
  text-transform: uppercase;
  color: var(--accent-text);
}
.card-name {
  font-family: var(--font-display);
  /* +200 over the tier's display weight. */
  font-weight: calc(var(--display-weight) + 200);
  font-size: var(--t-title);
  line-height: 1.2;
  word-break: break-word;
}
.card-meta {
  color: var(--ink-3);
  font-size: var(--t-small);
  font-style: var(--display-italic);
}
/* Unzip on an archive card, Open on a folder card (import/unpack.ts), centred
   in the room under the name. The card body ignores the pointer, so the button
   takes it back, as the style tile's swatches do. */
.card-unpack {
  margin: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px 6px 10px;
  border: var(--board-hairline) solid var(--rule);
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-small);
  cursor: pointer;
  pointer-events: auto;
}
.card-unpack:hover,
.card-unpack:focus-visible {
  border-color: var(--accent);
  color: var(--accent-text);
}
/* A far-zoom card is a block, and a click on it is a click on the board. */
#world.zoom-far .card-unpack { pointer-events: none; }
/* Text card body. Two variants sharing the same box: .card-text (monospaced,
   pre-wrapped source) and .card-md (prose in the board's own face). */
.card-text,
.card-md {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  margin: 0;
  white-space: pre-wrap;
  /* Wraps mid-word so minified lines do not widen the scroll box. */
  overflow-wrap: anywhere;
  tab-size: 2;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--ink-2);
  -webkit-mask-image: linear-gradient(180deg, #000 80%, transparent);
          mask-image: linear-gradient(180deg, #000 80%, transparent);
}
/* Markdown half: prose, not source. Block spacing crushed — a card is a
   glance, not a document. */
.card-md {
  font-family: inherit;
  white-space: normal;
  overflow-wrap: break-word;
}
.card-md > :first-child { margin-top: 0; }
.card-md h1, .card-md h2, .card-md h3,
.card-md h4, .card-md h5, .card-md h6 {
  margin: 0.6em 0 0.25em;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: calc(var(--display-weight) + 100);
  line-height: 1.2;
}
.card-md h1 { font-size: 1.4em; }
.card-md h2 { font-size: 1.18em; }
.card-md h3, .card-md h4,
.card-md h5, .card-md h6 { font-size: 1.04em; }
.card-md p { margin: 0 0 0.5em; }
.card-md ul, .card-md ol { margin: 0 0 0.5em; padding-left: 1.2em; }
.card-md li.md-task { list-style: none; margin-left: -1.1em; }
.card-md blockquote {
  margin: 0 0 0.5em;
  padding-left: 0.6em;
  border-left: 2px solid var(--rule);
}
.card-md hr { margin: 0.6em 0; border: 0; border-top: var(--hairline) solid var(--rule-2); }
.card-md code { font-family: var(--font-mono); font-size: 0.9em; }
.card-md pre { margin: 0 0 0.5em; overflow: hidden; }
/* Links are dead on a card — the viewer is where the document is read. */
.card-md a { color: inherit; pointer-events: none; text-decoration: underline; }
/* Tables cannot be legible in 300 world units; the viewer draws them. */
.card-md table { display: none; }
/* Tombstone card: what is drawn where a file used to be. Centred, unlike
   other cards, because a marker is centred on the space it marks. Muted
   throughout — the accent would announce an absence in the same voice the
   board announces its contents. */
.gone-card {
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 4px;
  color: var(--ink-3);
}
.gone-card .card-icon { color: var(--ink-3); }
.gone-card .card-name { color: var(--ink-3); }
/* Dashed rule — the outline of where something was (ghosts.css mirrors this
   for things not there yet). */
.gone-card::after { border-style: dashed; }

/* The bin glyph. Fraction of the card so a thumbnail tombstone and a poster
   tombstone are the same card at different scales. */
.gone-bin {
  flex: 0 1 auto;
  width: 34%;
  min-width: 28px;
  max-width: 88px;
  min-height: 0;
  aspect-ratio: 1;
  opacity: 0.55;
}
.gone-body, .gone-lid { fill: var(--ink-3); }
.gone-grip {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Ribs drawn in --paper-card (grooves) — ink on ink would be invisible. */
.gone-ribs {
  fill: none;
  stroke: var(--paper-card);
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* Swatch card: a colour sample on a plate. Paper behind the swatch is the
   neutral constant — a full-bleed swatch would be judged against its
   neighbours rather than against something neutral. */
.swatch-card {
  gap: 8px;
  align-items: stretch;
}

/* The well is a real <input type="color"> — undressed here to match the board.
   Two vendor spellings of the same two parts. */
.swatch-well {
  flex: 1;
  min-height: 0;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--swatch, #8a8a8a);
  /* Hairline edge so a pale swatch on pale card stock has a boundary. */
  box-shadow: 0 0 0 var(--board-hairline) color-mix(in srgb, var(--ink) 10%, transparent);
  cursor: pointer;
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}
.swatch-well::-webkit-color-swatch-wrapper { padding: 0; }
.swatch-well::-webkit-color-swatch { border: 0; border-radius: inherit; }
.swatch-well::-moz-color-swatch { border: 0; border-radius: inherit; }

/* ── The style tile ─────────────────────────────────────────────────────────
   The board's own look as a card. Three bands stacked; the middle one (chips)
   has a fixed height. Nothing here names a colour — the renderer hands
   live tokens via --chip so the tile follows palette changes. */
.style-tile-card {
  gap: 8px;
  align-items: stretch;
}
/* The caption plate would sit over the face names at the foot, so it rests
   hidden and comes back on hover, focus and rename, as a photograph's does
   (media.css). */
.item[data-type="style-tile"] .item-bar {
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.item[data-type="style-tile"]:is(:hover, :focus-within, .is-editing) .item-bar { opacity: 1; }

/* The card a cleaned board leaves (canvas/spotless.ts). One SVG face edge to
   edge; the "before" rectangles bring their own colours and everything else is
   the palette's. Under the pointer, or selected, the face gives way to a
   second line. */
.spotless-card {
  padding: 0;
  overflow: hidden;
}
.spotless-face {
  display: block;
  width: 100%;
  height: 100%;
}
.spotless-frame { fill: var(--paper); }
.spotless-after {
  fill: var(--paper-card);
  stroke: var(--item-border);
  stroke-width: 0.75;
}
.spotless-said {
  fill: var(--ink);
  font-family: var(--font-display);
  font-size: 18px;
  text-anchor: middle;
}
.spotless-count {
  fill: var(--ink-3);
  font-family: var(--font-body);
  font-size: 12px;
  text-anchor: middle;
}
.spotless-hover {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 12%;
  background: var(--paper-card);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1.3;
  text-align: center;
  text-wrap: balance;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
  pointer-events: none;
}
.item[data-type="card"]:is(:hover, .is-selected) .spotless-hover { opacity: 1; }
/* The song button: a bare note by the corner, arriving with the hover line.
   Three things each lose it the press. `pointer-events: auto`, because
   .item-body turns them off for everything in a card. z-index 3, over the
   grips (2) like .vbig: the press selects the card, which puts the grips up
   under the pointer before the release, and the click lands on the grip. And
   in off the corner, which is the resize grip's to keep. */
.spotless-song {
  position: absolute;
  right: 14px;
  bottom: 30px;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease), color var(--dur-fast) var(--ease);
}
/* Selected as well, like the line, which is the only hover a phone has. */
.item[data-type="card"]:is(:hover, .is-selected) .spotless-song,
.spotless-song:focus-visible { opacity: 1; }
.spotless-song:hover,
.spotless-song:focus-visible { color: var(--accent-text); }
#world.zoom-far .spotless-song { pointer-events: none; }
/* Its name is "Spotless", and the face already says more than that. */
.item[data-type="card"]:not(.is-editing) .item-bar { display: none; }

.style-tile-strip {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 4px;
}

/* Cover-cropped into equal boxes — a style tile is about register, not
   individual aspect ratios. */
.style-tile-shot {
  flex: 1;
  min-width: 0;
  border-radius: var(--radius-sm);
  background-image: var(--shot);
  background-size: cover;
  background-position: center;
  box-shadow: 0 0 0 var(--board-hairline) color-mix(in srgb, var(--ink) 10%, transparent);
}

/* Two rows of four, each chip separate and named underneath. */
.style-tile-swatches {
  flex: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px 4px;
}

.style-tile-swatch {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-3);
  font: 500 10px/1.2 var(--font-mono);
  text-align: left;
  cursor: copy;
  /* The card's body takes no pointer; a swatch is the exception, the bargain
     the link card's anchor makes. The gaps and the rest still drag the card. */
  pointer-events: auto;
}
/* A far-zoom tile is a coloured block, and a click on it is a click on the board. */
#world.zoom-far .style-tile-swatch { pointer-events: none; }

.style-tile-chip {
  height: 22px;
  border-radius: var(--radius-sm);
  background: var(--chip);
  box-shadow: 0 0 0 var(--board-hairline) color-mix(in srgb, var(--ink) 10%, transparent);
}

.style-tile-name,
.style-tile-hex {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* The hex takes the name's place while the pointer is on the swatch. */
.style-tile-hex { display: none; color: var(--ink); }
.style-tile-swatch:is(:hover, :focus-visible) .style-tile-hex { display: block; }
.style-tile-swatch:is(:hover, :focus-visible) .style-tile-name { display: none; }

.style-tile-type {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/* Each name set in the face it names. Clipped — a long family name is the one
   thing that could push the bands out of the box. */
.style-tile-face {
  font-family: var(--face);
  color: var(--ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.style-tile-face:first-child {
  font-size: var(--t-body);
  color: var(--ink);
}

.style-tile-face:last-child { font-size: var(--t-small); }

/* Hex readout. Mono because it is a value, not a word. */
.swatch-hex {
  flex: none;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--t-small);
  letter-spacing: calc(0.04em * var(--ls-scale));
  color: var(--ink-2);
}

/* ── The font specimen ───────────────────────────────────────────────────────
   A dropped face, shown as its own letters. The specimen is SVG text fitted to
   the card by the renderer (viewBox + preserveAspectRatio="meet"), so the font
   size is dynamic and follows every resize with no script. The family name sits
   under it only when the specimen shows custom words instead of the name. */
.font-card {
  gap: 8px;
  align-items: stretch;
}
.font-specimen {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
}
/* The fitted mark fills the box; text inherits the board ink as its fill. */
.font-specimen svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: var(--ink);
}
/* The inline editor swapped in for the SVG while the specimen is edited (see
   editFontText in canvas/items.ts). A plain, legible line rather than the fitted
   mark - typing into a glyph scaled to the card is not typing anyone can aim. */
.font-edit {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--t-title);
  line-height: 1.2;
  color: var(--ink);
  overflow-wrap: anywhere;
  outline: none;
}
.font-name {
  flex: none;
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--t-small);
  font-style: var(--display-italic);
  color: var(--ink-2);
  /* One line: a long family name is clipped, not allowed to shove the specimen
     off its own card. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* Dimmed by the tag filter. Faded, not hidden — the board keeps showing
   everything. No pointer-events change: a card you can see but cannot touch
   is worse than either showing it or not. The transition is in .item's list
   above. */
.item.is-filtered,
.item-shadow.is-filtered { opacity: 0.16; }
/* A selected card comes most of the way back — you reached past the fade. */
.item.is-filtered.is-selected { opacity: 0.75; }

/* ── The board behind a card whose threads you are following ────────────────
   Everything the lines do not involve steps back. 0.6, not the threads' 0.18:
   a photograph at 0.18 is gone, not quiet. :not(.is-filtered) because a
   filtered card is already at 0.16 and this rule would bring it back.
   No `transition` here — opacity is in the lists on .item above and
   .item-shadow in canvas.css. */
:root.board-lifted .item:not([data-lit]):not(.is-filtered),
:root.board-lifted .item-shadow:not([data-lit]):not(.is-filtered) { opacity: 0.6; }

/* ── A cut-out: the picture without the card ──────────────────────────────
   A transparent-background image. The paint goes, the box stays — hit-testing,
   marquee, ring and grips all measure this element's rectangle. */
.item[data-bare] {
  background: transparent;
}
/* Hover ring off — it would draw the empty rectangle back on hover. The
   selection ring stays: a selected item must show what you have got. */
.item[data-bare]:hover { box-shadow: none; }
/* Far-zoom edge off for the same reason. */
#world.zoom-far .item[data-bare]::after { box-shadow: none; }
/* Caption plate off — "no card" means no card. :not(.is-editing) because F2
   renames by putting the caret in this very label. */
.item[data-bare]:not(.is-editing) .item-bar { display: none; }

/* Fallback without color-mix() (WebKit < 16.2). */
@supports not (color: color-mix(in srgb, red, blue)) {
  .swatch-well,
  .style-tile-shot,
  .style-tile-chip {
    box-shadow: 0 0 0 var(--board-hairline) rgb(var(--ink-c) / 10%);
  }
}
