/* Conspiracy Board — experimental profile canvas, kept in its own
   stylesheet on purpose (see corkboard.html's comment). Only reaches into
   app.css for shared tokens (--card, --red, --gold, fonts); every rule
   below is scoped under .corkboard-* / #corkboard-* so nothing here can
   bleed into the real app. */

.corkboard-body {
  background: var(--black);
  color: var(--white);
  font-family: var(--body);
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-tap-highlight-color: transparent;
}

.corkboard-app { display: flex; flex-direction: column; min-height: 100vh; }
.corkboard-app.hidden,
.corkboard-loggedout.hidden,
.corkboard-loading.hidden { display: none; }

.corkboard-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  color: var(--muted);
  font-family: var(--mono);
  font-size: var(--text-sm);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.corkboard-loggedout {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  min-height: 100vh;
  text-align: center;
  padding: var(--space-6);
  color: var(--muted);
}

/* ---------- Topbar ---------- */
/* Compact single row on purpose — this is a phone-first screen (see the
   rest of the app's manifest/safe-area handling), so the header can't cost
   more than about one thumb's reach of vertical space. env(safe-area-inset-top)
   clears the notch/status bar the same way the real app's --safe-top does. */
.corkboard-topbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: calc(var(--space-3) + env(safe-area-inset-top)) var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--card-2);
  background: var(--card);
}
.corkboard-back,
.corkboard-icon-btn {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 999px;
  background: var(--card-2);
  border: none;
  color: var(--white);
  text-decoration: none;
  font-size: 1.1rem;
  cursor: pointer;
}
.corkboard-icon-btn:active,
.corkboard-back:active { transform: scale(0.92); }
.corkboard-icon-btn-danger { color: var(--red); }
.corkboard-icon-btn-active { background: var(--red-dim); color: var(--red); }
.corkboard-title {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.corkboard-title h1 {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--text-lg);
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.corkboard-kicker {
  display: block;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--red);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.corkboard-actions { display: flex; gap: var(--space-2); flex: 0 0 auto; }

/* js/profile-board.js's in-frame edit FAB (Board sub-tab, view mode only) —
   not used by the standalone cork/index.html prototype, which has its own
   topbar/.corkboard-actions above instead. Overlaid ON the stage itself
   (inside the frame), not a separate toolbar row above it — tapping it hands
   this board off to the dedicated Board Editor screen (css/app.css's
   .board-editor-screen); there's nothing to tap once you're already there,
   so it only shows in view mode. */
.corkboard-edit-fab {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: 4;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 999px;
  border: none;
  background: rgba(19, 19, 21, 0.78);
  color: var(--white);
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.corkboard-edit-fab:active { transform: scale(0.92); }
.corkboard-app:not(.corkboard-view-mode) .corkboard-edit-fab { display: none; }

/* Invisible Ink's "night vision" toggle (2026-07-23) — its OWN corner
   (top-left), separate from the edit FAB's (top-right), so both can be on
   screen together on your own board: this is available in self view too,
   not just on someone else's board. Hidden via .hidden (js/profile-board.js's
   updateNightVisionBtn) for any viewer who doesn't own Invisible Ink at
   all — same total-mystery posture as the rest of the mechanic, nobody
   without the ink should even see a button here, board owner included.
   .is-active is the "lights off" state itself; see .corkboard-night-vision
   below for what that actually does to the board. */
.corkboard-nightvision-fab {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 10;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 999px;
  border: none;
  background: rgba(19, 19, 21, 0.78);
  color: var(--white);
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.corkboard-nightvision-fab.hidden { display: none; }
.corkboard-nightvision-fab.is-active {
  background: rgba(255, 20, 147, 0.4);
  box-shadow: 0 0 10px 2px rgba(255, 20, 147, 0.75);
}
.corkboard-nightvision-fab:active { transform: scale(0.92); }

/* Multi-page boards (2026-07-30) — a page beyond the first gets a darkened
   pill-shaped title at top-center (default "Board N"), plus a small "+
   Board" button next to it (own board only) to add another. Prev/next live
   in the stage's bottom corners instead, only shown once there's actually
   somewhere to go (js/profile-board.js's updatePageBar). */
.corkboard-page-bar {
  position: absolute;
  top: var(--space-2);
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.corkboard-page-pill {
  display: inline-block;
  background: rgba(0, 0, 0, 0.68);
  color: var(--white);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  max-width: 40vw;
}
.corkboard-page-pill.hidden { display: none; }
.corkboard-page-pill-text {
  outline: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}
/* Shared small dark pill-button look — .corkboard-page-add-btn (Profile
   Board's "+ Board", own-board-editor only) and .hub-board-add-btn (Hub
   Corkboard's "+ Note") both use this base and nothing else in common: the
   two live in different components with different visibility rules, so
   each adds its own on top rather than sharing one combined class. */
.corkboard-pill-btn {
  background: rgba(19, 19, 21, 0.78);
  color: var(--white);
  border: none;
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.66rem;
  padding: 0.3rem 0.7rem;
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  cursor: pointer;
  white-space: nowrap;
}
.corkboard-pill-btn:active { transform: scale(0.94); }
/* "+ Board" only makes sense INSIDE the dedicated Board Editor (spec: "in
   editor") — .corkboard-view-mode is the plain inline Profile-tab display,
   the opposite of that (see .corkboard-edit-fab's own hiding rule just
   above, which this deliberately mirrors in the other direction). */
.corkboard-app.corkboard-view-mode .corkboard-page-add-btn { display: none; }

.corkboard-page-nav {
  position: absolute;
  bottom: var(--space-2);
  z-index: 4;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: none;
  background: rgba(19, 19, 21, 0.78);
  color: var(--white);
  font-size: 1.2rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.corkboard-page-nav.hidden { display: none; }
.corkboard-page-nav:active { transform: scale(0.92); }
.corkboard-page-nav-prev { left: var(--space-2); }
.corkboard-page-nav-next { right: var(--space-2); }

/* Page-to-page swipe cue (2026-07-31) — same "content slides in from
   whichever edge it arrived from" idea as css/app.css's tab-swipe-from-
   right/-left, applied here to the board surface instead of a tab panel:
   before this, switching pages via Prev/Next was an instant, un-animated
   content swap, the one navigation surface in the app with no transition
   language at all. */
@keyframes corkboard-page-swipe-from-right {
  from { opacity: 0; transform: translateX(18px); }
  to { opacity: 1; transform: translateX(0); }
}
@keyframes corkboard-page-swipe-from-left {
  from { opacity: 0; transform: translateX(-18px); }
  to { opacity: 1; transform: translateX(0); }
}
.corkboard-page-swipe-from-right { animation: corkboard-page-swipe-from-right 0.22s var(--ease); }
.corkboard-page-swipe-from-left { animation: corkboard-page-swipe-from-left 0.22s var(--ease); }

/* Hub Corkboard (2026-07-30, js/hub-board.js) — a much smaller stage than
   the Profile Board's own (js/missions.js's Hub Sheet is a centered modal,
   .modal-card, not a full tab), and its own "+ Note" add button reuses the
   page-bar button's pill styling but needs its own top-right placement
   instead of that bar's flex-row layout (there's no page bar here at all —
   a hub board is always a single page). */
#hub-sheet-board .corkboard-stage {
  min-height: 220px;
  margin: var(--space-2) 0 0;
}
.hub-board-add-btn {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: 4;
}
.hub-sheet-board-label {
  display: block;
  margin-top: var(--space-3);
}

/* The "lights off" effect itself — a dark blue wash over the whole board
   (replacing its usual cork texture entirely, not just tinting it) with
   every pinned node dimmed to near-invisibility EXCEPT its own
   .corkboard-note-secret strip, which is deliberately excluded from the dim
   and given its own bright pink glow instead — "leaving only invisible
   stuff glowing bright pink." Dimming each node's own direct children
   (rather than darkening via a stacking z-index layer above every node)
   sidesteps a real CSS trap: .corkboard-node establishes its own stacking
   context (it's position:absolute with a z-index), so a child's z-index can
   never out-rank a sibling OF THE NODE ITSELF no matter how high it's set —
   an overlay positioned above the node array by z-index alone would sit
   above secret strips too, not just the plain content around them.
   TWO selectors, not one, because of .corkboard-note-stack (the grid-stack
   overlay wrapper a note/note-item's card and secret both live inside) —
   a badge/card/sticker has no such wrapper and is dimmed directly as a
   child of .corkboard-node, but a note's OWN card is one level deeper, and
   dimming the stack itself as a whole (the bug this replaced) applies the
   filter to everything inside it, secret included, since CSS filter
   affects an entire subtree, not just the element it's set on. */
.corkboard-night-vision .corkboard-board {
  background: #050a1f;
  transition: background 0.3s ease;
}
.corkboard-night-vision .corkboard-strings { opacity: 0.12; }
.corkboard-night-vision .corkboard-node > *:not(.corkboard-note-secret):not(.corkboard-note-stack),
.corkboard-night-vision .corkboard-node > .corkboard-note-stack > *:not(.corkboard-note-secret) {
  filter: brightness(0.18) saturate(0.35);
  transition: filter 0.3s ease;
}
.corkboard-night-vision .corkboard-note-secret {
  background: rgba(255, 20, 147, 0.3);
  border-color: #ff36b8;
  color: #ffe6f7;
  box-shadow: 0 0 16px 4px rgba(255, 20, 147, 0.9), 0 0 3px rgba(255, 255, 255, 0.6);
  /* A single soft pink shadow (the original version here) barely reads as
     a glow at all — its color is too close to the background's own pink
     tint to create real contrast. Classic neon-sign layering instead: a
     tight near-white core shadow first (what actually reads as "glowing
     hot"), then two wider pink shadows stacked behind it for the halo. */
  text-shadow:
    0 0 2px rgba(255, 255, 255, 0.9),
    0 0 6px rgba(255, 20, 147, 1),
    0 0 14px rgba(255, 20, 147, 0.85);
}
.corkboard-night-vision .corkboard-note-secret:empty::before { color: rgba(255, 210, 240, 0.7); }

/* Instructions as a native <details> — costs one line until an agent
   actually wants to read it, no JS needed to expand/collapse. */
.corkboard-hint {
  margin: var(--space-3) var(--space-4) 0;
  color: var(--muted);
  font-size: var(--text-sm);
}
.corkboard-hint summary {
  cursor: pointer;
  font-size: var(--text-xs);
  color: var(--muted);
  list-style-position: outside;
}
.corkboard-hint p {
  margin: var(--space-2) 0 0;
  line-height: 1.5;
}

/* ---------- Board ---------- */
.corkboard-stage {
  position: relative;
  flex: 1 1 auto;
  margin: var(--space-3) var(--space-4) 0;
  /* clamp rather than a flat vh: on a short phone viewport (address bar +
     topbar + hint + tray all competing for the same screen) a flat 60vh
     left no room for the tray without scrolling past the board first. */
  min-height: clamp(280px, 52vh, 520px);
  border-radius: var(--radius-lg);
  /* visible, not hidden: a large-displayScale sticker (see
     .corkboard-node-sticker-img's inline transform:scale, set from
     flairDefinitions) is meant to bleed past the board's own edges rather
     than get invisibly cropped there — .corkboard-board below carries its
     own border-radius so the wood-frame corners still read correctly
     without the parent needing to clip. */
  overflow: visible;
  box-shadow: var(--shadow-card), inset 0 0 0 10px #5b3a20, inset 0 0 0 13px #2e1c0f;
}

.corkboard-board {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-color: #b9895a;
  background-image:
    radial-gradient(circle at 12% 22%, rgba(0, 0, 0, 0.16) 0, transparent 2.4%),
    radial-gradient(circle at 78% 14%, rgba(0, 0, 0, 0.14) 0, transparent 2%),
    radial-gradient(circle at 34% 68%, rgba(0, 0, 0, 0.16) 0, transparent 2.2%),
    radial-gradient(circle at 91% 55%, rgba(0, 0, 0, 0.13) 0, transparent 2%),
    radial-gradient(circle at 58% 40%, rgba(0, 0, 0, 0.15) 0, transparent 2.6%),
    radial-gradient(circle at 20% 88%, rgba(255, 255, 255, 0.06) 0, transparent 2%),
    radial-gradient(circle at 66% 84%, rgba(255, 255, 255, 0.05) 0, transparent 2.4%),
    radial-gradient(circle at 8% 55%, rgba(255, 255, 255, 0.05) 0, transparent 2%);
  background-size: 220px 220px;
}

.corkboard-strings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
}
.corkboard-strings path.corkboard-string-visible {
  fill: none;
  stroke: #b3141f;
  stroke-width: 2.5;
  stroke-linecap: round;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}
.corkboard-strings path.corkboard-string-hit {
  fill: none;
  stroke: transparent;
  stroke-width: 16;
  stroke-linecap: round;
  pointer-events: stroke;
  cursor: crosshair;
}

/* ---------- Pinned nodes (badges / flairs / notes) ---------- */
.corkboard-node {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -50%) rotate(var(--tilt, 0deg));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 76px;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.corkboard-node:active { cursor: grabbing; }

/* Tray-only framed look (badge/flair inventory tiles) — the board itself
   deliberately does NOT use this for stickers anymore, see
   .corkboard-node-sticker-img below. */
.corkboard-node-card {
  width: 68px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--white);
  border-radius: var(--radius-xs);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.corkboard-node-card .corkboard-node-icon { font-size: 2rem; line-height: 1; }
.corkboard-node-card img { width: 100%; height: 100%; object-fit: cover; }

.corkboard-node-label {
  max-width: 100%;
  padding: 1px 5px;
  background: rgba(8, 8, 10, 0.72);
  color: var(--white);
  font-size: 0.62rem;
  line-height: 1.2;
  text-align: center;
  border-radius: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Badge design, pinned to the board: an enamel pin — just the emoji, a
   hard all-around outline (drop-shadow stacked in 4 directions rather than
   a true vector stroke, which emoji glyphs can't take reliably across
   browsers) plus a soft glossy highlight, no card, no name tag. Real
   enamel pins have no visible pin mechanism on the front (it's on the
   back) — badges get no red tack for the same reason, see
   .corkboard-node-badge's tack override below. */
.corkboard-node-badge-icon-wrap {
  position: relative;
  width: 50px;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.corkboard-node-badge-icon-wrap .corkboard-node-icon {
  font-size: 2.6rem;
  line-height: 1;
  filter:
    drop-shadow(1px 0 0 rgba(0, 0, 0, 0.6))
    drop-shadow(-1px 0 0 rgba(0, 0, 0, 0.6))
    drop-shadow(0 1px 0 rgba(0, 0, 0, 0.6))
    drop-shadow(0 -1px 0 rgba(0, 0, 0, 0.6))
    drop-shadow(0 3px 4px rgba(0, 0, 0, 0.5));
}
/* The sheen used to be a flat gradient over the whole (much larger)
   wrapper box, which showed as a hazy rectangle bleeding well past the
   glyph's actual silhouette. It's a real element now (not ::after —
   pseudo-elements can't take an inline mask-image directly), masked per
   node to that badge's own emoji character (rendered as blurred SVG
   <text>, so the mask edge is soft/feathered rather than a crisp glyph
   outline) — see js's emojiSheenMaskUrl. The wrapper is also sized close
   to the glyph's own footprint now instead of the full 76px node width,
   so even the mask's bounding box roughly tracks the icon. */
.corkboard-node-badge-sheen {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.85), transparent 48%);
  mix-blend-mode: overlay;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  pointer-events: none;
}
/* No visible pin for an enamel badge — but the string-drag start point
   stays exactly where the tack used to be (same position/hit-area, just
   invisible), so connecting a badge to another item still works the same
   way as everything else. (js/profile-board.js's ported version doesn't
   render a tack for a badge at all anymore — see its own
   beginBadgeHoldGesture — so this rule is standalone-cork-page-only now;
   js/corkboard.js there still renders one.) */
.corkboard-node-badge .corkboard-tack { opacity: 0; }

/* Press-and-hold-to-string feedback (js/profile-board.js's
   beginBadgeHoldGesture) — a badge has no visible thumbtack to grab, so
   holding it still instead glows brighter the longer you hold, telling you
   something's about to happen before it does. Only ever added by the ported
   board, not the standalone cork/index.html prototype's own js/corkboard.js. */
@keyframes corkboard-node-charge {
  from { filter: brightness(1); }
  to { filter: brightness(1.6) drop-shadow(0 0 7px rgba(179, 20, 31, 0.85)); }
}
.corkboard-node-charging .corkboard-node-badge-icon-wrap {
  animation: corkboard-node-charge 0.45s ease-in forwards;
}

/* Playing Card design, pinned to the board (a card becomes a draggable
   candidate the moment you hold one — see js/profile-board.js's
   refreshItems — js/items.js's `pinnable` flag on `card`): a real card
   face, colored the way a real card's pips are, rather than a photo or an
   emoji, so the suit's real color is legible at a glance, matching the
   "real value" name (cardLabel) the item is described by everywhere else
   it shows up. Center content varies by rank (js/lib/corkboard-engine.js's
   cardCenterContentHtml) — an Ace is one big suit glyph
   (.corkboard-playing-card-suit below), 2-10 gets a real pip grid
   (.corkboard-playing-card-pips), J/Q/K gets the royal silhouette
   (.corkboard-playing-card-royal) — the corner rank/suit index is the one
   piece every rank always shows. */
.corkboard-node-playing-card { width: 60px; }
.corkboard-playing-card {
  position: relative;
  width: 56px;
  height: 78px;
  background: var(--white);
  border-radius: 4px;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45);
  font-family: var(--body);
}
.corkboard-playing-card-red { color: #b3141f; }
.corkboard-playing-card-black { color: #17171a; }
.corkboard-playing-card-corner {
  position: absolute;
  font-size: 0.6rem;
  line-height: 1.05;
  font-weight: 700;
  text-align: center;
}
.corkboard-playing-card-corner-top { top: 3px; left: 4px; }
/* Rotated 180° — the same convention every real deck uses so the rank/suit
   reads right-side-up no matter which end of the card faces you. */
.corkboard-playing-card-corner-bottom { bottom: 3px; right: 4px; transform: rotate(180deg); }
.corkboard-playing-card-suit {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
}
/* A pip's own {x,y}% (js's CARD_PIP_LAYOUTS) is relative to this inset
   box, not the card's full bleed — real cards leave the same kind of
   margin around their pip field so it doesn't crowd the corner indices. */
.corkboard-playing-card-pips { position: absolute; inset: 10px 6px; }
.corkboard-playing-card-pip { position: absolute; font-size: 0.5rem; line-height: 1; }
/* The face-card (J/Q/K) icon (game-icons.net, see js's own credit comment
   + THIRD_PARTY_NOTICES.md) — fixed square px size (its source viewBox is
   itself square) centered in the card regardless of the card's own portrait
   proportions, rather than an inset box that would force/distort it.
   fill: currentColor picks up whichever of .corkboard-playing-card-red/-black
   is set on the card, same as every other piece of this card's own color
   already does. */
.corkboard-playing-card-royal {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 34px;
  height: 34px;
  transform: translate(-50%, -50%);
  fill: currentColor;
}

/* Note design, pinned to the board (a note becomes a draggable candidate
   the moment you hold one, same as a card — see js/profile-board.js's
   refreshItems, js/items.js's `pinnable` flag): the same yellow
   sticky-paper look the board's own freeform notes use
   (.corkboard-note-color-yellow, reused as-is here) but read-only — deliberately
   its own class rather than .corkboard-note-card, so it never picks up that
   class's contenteditable/threshold-drag gesture built for typing in place.
   This one's text comes from the item itself (js/inventory.js's Read/reply
   thread), not something you edit on the board. */
.corkboard-node-note-item { width: 92px; }
.corkboard-pinned-note {
  width: 92px;
  min-height: 74px;
  height: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #2a2109;
  font-family: 'Caveat', cursive;
  font-size: 0.95rem;
  line-height: 1.2;
  padding: var(--space-2);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45);
  border-radius: 2px;
  overflow-wrap: break-word;
  text-align: center;
}

/* Sticker design, pinned to the board: no card, no frame, no label — "just
   as themselves." A drop-shadow is the only concession, so a transparent
   PNG still reads as sitting on top of the cork rather than floating flat. */
.corkboard-node-sticker-img {
  width: 76px;
  height: 76px;
  object-fit: contain;
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.5));
  pointer-events: none;
}

/* Visual dot stays small (15px) so it doesn't dominate a 68px card, but its
   actual touch target (via ::before) is a lot bigger — 15px alone is well
   under the ~40px a thumb can reliably hit. */
.corkboard-tack {
  position: absolute;
  top: -6px;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 15px;
  height: 15px;
  border-radius: 999px;
  background: radial-gradient(circle at 35% 30%, #ff5b5b, #b3141f 65%, #7a0d16);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  cursor: crosshair;
  touch-action: none;
  z-index: 4;
}
.corkboard-tack::before {
  content: "";
  position: absolute;
  inset: -13px;
}

.corkboard-node-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  border: none;
  background: var(--card);
  color: var(--white);
  font-size: 0.6rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  touch-action: none;
  z-index: 4;
}
.corkboard-node-remove::before {
  content: "";
  position: absolute;
  inset: -11px;
}

/* Sticky note node — wider than a badge card, editable text, better drag
   surface. The card itself is now draggable (see js's threshold-drag: a
   real move cancels any text focus, a plain tap still edits) — the grip
   bar is a secondary, always-immediate handle for anyone who wants a sure
   thing to grab. */
.corkboard-node-note { width: 108px; }
.corkboard-note-grip {
  width: 100%;
  height: 10px;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 2px 2px 0 0;
  cursor: grab;
  touch-action: none;
}
.corkboard-note-card {
  width: 108px;
  min-height: 88px;
  height: auto;
  /* Vertically centers short notes instead of pinning them to the top —
     text-align (the alignment bopper) still governs the horizontal axis;
     this is just the default vertical position, not something either
     bopper controls. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: #2a2109;
  font-family: 'Caveat', cursive;
  font-size: 1rem;
  line-height: 1.2;
  padding: var(--space-2);
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45);
  border-radius: 2px;
  outline: none;
  overflow-wrap: break-word;
  cursor: text;
  touch-action: none;
}
.corkboard-note-card:empty::before { content: attr(data-placeholder); color: rgba(42, 33, 9, 0.5); }

/* .corkboard-note-stack (2026-07-23) grid-stacks a note's visible content
   (.corkboard-note-card / .corkboard-pinned-note / .corkboard-lightbox-note)
   and its .corkboard-note-secret layer into the SAME cell, so the secret
   overlays the visible note in place — the same physical note, not a
   second one glued on underneath — while the cell still auto-sizes to
   whichever of the two is taller. A plain position:absolute overlay can't
   do that without either clipping a longer secret or overflowing its box;
   grid-stacking sidesteps the problem entirely. Every direct child fills
   the one cell (justify/align-items default to stretch), so the secret
   naturally matches whatever width the visible note underneath it uses
   (108px/92px/100%, depending on which of the three note contexts it's in)
   without needing its own width rule at all. */
.corkboard-note-stack { display: grid; }
.corkboard-note-stack > * { grid-area: 1 / 1; }

/* Invisible Ink's own secret layer (2026-07-23, js/lib/corkboard-engine.js's
   boardNodeContentHtml) — only ever rendered at all if the CURRENT VIEWER
   holds the ink (own board or someone else's). Semi-transparent on purpose
   (not a solid fill) — it reads as a tinted sheet laid over the plain note
   rather than fully replacing it, the ghosted plain text still faintly
   visible underneath through the violet/UV tint, same reasoning the Note
   Thread modal's own .note-thread-secret (css/app.css) already uses for
   its color.
   `z-index: 1` is load-bearing, not decorative (2026-07-24 fix) — being
   LATER in DOM order inside the grid-stack is NOT enough on its own to
   guarantee this paints on top. Night vision's own dimming rule
   (.corkboard-night-vision .corkboard-node > .corkboard-note-stack > *)
   applies `filter` to the plain card, and `filter` unconditionally
   promotes an element into its own stacking context — which made the
   FILTERED card paint above this later-but-unfiltered sibling despite DOM
   order, verified with an isolated grid repro outside the app entirely.
   An explicit z-index here is what actually wins that tie unambiguously,
   regardless of which sibling happens to pick up a stacking-context-
   creating property like filter/transform/opacity in the future. */
.corkboard-note-secret {
  min-height: 40px;
  height: auto;
  color: #e4d6ff;
  background: rgba(88, 40, 160, 0.72);
  border: 1px dashed rgba(201, 182, 255, 0.55);
  /* Legibility fix (2026-07-30, user feedback: "can't read") — this used to
     be locked to a small italic cursive regardless of the visible layer's
     own font choice. font-family now comes inline per-note (js/lib/
     corkboard-engine.js's noteFontFamily, defaulting to "typed" rather than
     "hand"); this is only the pre-inline-style fallback. Sized bigger than
     the visible layer on purpose — a secret is meant to be read carefully
     once you've found it, not squinted at. */
  font-family: var(--body);
  font-size: 1.2rem;
  line-height: 1.3;
  padding: var(--space-2);
  border-radius: 2px;
  outline: none;
  overflow-wrap: break-word;
  cursor: text;
  touch-action: none;
  position: relative;
  z-index: 1;
}
.corkboard-note-secret:empty::before { content: attr(data-placeholder); color: rgba(228, 214, 255, 0.55); }

/* Four preset colors — a real sticky-note pad always has a few, not one. */
.corkboard-note-color-yellow { background: #f4e6a1; }
.corkboard-note-color-pink { background: #f6c7dc; }
.corkboard-note-color-blue { background: #bfe2f0; }
.corkboard-note-color-green { background: #c8e8bd; }

.corkboard-note-color-btn {
  position: absolute;
  bottom: -6px;
  right: -6px;
  width: 15px;
  height: 15px;
  border-radius: 999px;
  border: 2px solid var(--card);
  cursor: pointer;
  touch-action: none;
  z-index: 4;
}
.corkboard-note-color-btn::before {
  content: "";
  position: absolute;
  inset: -10px;
}

/* Alignment/font "boppers" — same corner-dot pattern as the color button
   above and the tack/remove buttons, just claiming the two corners those
   don't use (top-right is remove, bottom-right is color, top-center is the
   tack). Each shows its own CURRENT state (the letter for alignment, the
   actual selected font for the "Aa" preview) and cycles on tap, same as
   the color button already does. */
.corkboard-note-align-btn,
.corkboard-note-font-btn {
  position: absolute;
  left: -6px;
  width: 17px;
  height: 17px;
  border-radius: 999px;
  border: none;
  background: var(--card);
  color: var(--white);
  font-family: var(--mono);
  font-size: 0.55rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  touch-action: none;
  z-index: 4;
}
.corkboard-note-font-btn { top: -6px; }
/* While night vision is toggled on and you're actively writing invisible
   ink, this same bopper switches to controlling THAT layer's font instead
   of the visible note's — tinted to match the secret layer's violet so it's
   obvious which text it's about to change. */
.corkboard-note-font-btn-secret {
  background: rgba(88, 40, 160, 0.9);
  color: #e4d6ff;
}
.corkboard-note-align-btn { bottom: -6px; }
.corkboard-note-align-btn::before,
.corkboard-note-font-btn::before {
  content: "";
  position: absolute;
  inset: -10px;
}
/* The align bopper's three-bar icon (js/lib/corkboard-engine.js's
   NOTE_ALIGN_GLYPH) — fill: currentColor picks up the button's own
   color: var(--white). */
.corkboard-align-icon { width: 11px; height: 9px; fill: currentColor; display: block; }

/* Dragging-a-new-item ghost, and the live rubber-band string preview */
.corkboard-drag-ghost {
  position: fixed;
  z-index: 999;
  pointer-events: none;
  opacity: 0.9;
  transform: translate(-50%, -50%);
}
.corkboard-note-ghost { width: 60px; min-height: 60px; }

/* ---------- Tray ---------- */
.corkboard-tray-wrap {
  margin: var(--space-3) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom));
}
.corkboard-tray-summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
}
.corkboard-tray-summary::-webkit-details-marker { display: none; }
.corkboard-tray-summary::before {
  content: "▸";
  display: inline-block;
  transition: transform 0.15s var(--ease);
}
.corkboard-tray-wrap[open] > .corkboard-tray-summary::before {
  transform: rotate(90deg);
}
.corkboard-tray {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
  padding: var(--space-3);
  background: var(--card);
  border: 1px solid var(--card-2);
  border-radius: var(--radius-md);
  min-height: 84px;
}
.corkboard-tray-empty {
  color: var(--muted);
  font-size: var(--text-sm);
  align-self: center;
}
.corkboard-tray .corkboard-node {
  position: static;
  transform: none;
}
.corkboard-tray .corkboard-node .corkboard-node-card { box-shadow: var(--shadow-card); }

/* The infinite "New Note" tile — always the tray's first item, never
   removed (dragging it out spawns a fresh note rather than depleting a
   stock), so it gets its own dashed "bottomless pool" affordance. */
.corkboard-tray-note-card {
  display: flex;
  align-items: center;
  justify-content: center;
  outline: 2px dashed rgba(42, 33, 9, 0.35);
  outline-offset: -4px;
}
.corkboard-tray-note-plus {
  font-family: var(--body);
  font-size: 1.6rem;
  line-height: 1;
  color: rgba(42, 33, 9, 0.6);
}

.corkboard-status {
  position: fixed;
  left: 50%;
  bottom: calc(var(--space-5) + env(safe-area-inset-bottom));
  transform: translateX(-50%) translateY(12px);
  background: var(--card);
  border: 1px solid var(--card-2);
  color: var(--white);
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  box-shadow: var(--shadow-modal);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  z-index: 1000;
}
.corkboard-status.corkboard-status-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------- Storage box (Instagram-story-style drop target) ---------- */
/* Hidden entirely until a pinned item starts moving, then fades in just
   below the board's OWN current bottom edge — drop a pin (or a note) on
   it to put it away, exactly like dragging a story sticker onto the trash
   can. `top` is set from JS (positionStorageBox(), reading
   stage.getBoundingClientRect().bottom) rather than anchored to the
   viewport bottom via CSS, since the board's actual bottom edge moves
   with the tray collapsing/expanding, the hint expanding, and viewport
   height changes — a fixed viewport-relative offset drifted away from the
   board entirely in exactly those cases. */
.corkboard-storage-box {
  position: fixed;
  left: 50%;
  top: 0;
  transform: translate(-50%, -12px) scale(0.9);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
  background: rgba(19, 19, 21, 0.92);
  border: 1px solid var(--card-2);
  border-radius: var(--radius-lg);
  color: var(--muted);
  font-size: var(--text-xs);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
  z-index: 1001;
}
.corkboard-storage-box-icon { font-size: 1.6rem; line-height: 1; }
.corkboard-storage-box-visible {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}
.corkboard-storage-box-hover {
  transform: translate(-50%, 0) scale(1.18);
  color: var(--white);
  border-color: var(--red);
  background: rgba(51, 10, 15, 0.92);
}

/* ---------- Lightbox (non-edit view) ---------- */
.corkboard-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  background: rgba(0, 0, 0, 0.72);
}
.corkboard-lightbox.hidden { display: none; }
.corkboard-lightbox-card {
  position: relative;
  max-width: 320px;
  width: 100%;
  background: var(--card);
  border: 1px solid var(--card-2);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  padding: var(--space-6) var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
}
.corkboard-lightbox-close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 999px;
  border: none;
  background: var(--card-2);
  color: var(--white);
  font-size: 1rem;
  cursor: pointer;
}
.corkboard-lightbox-icon { font-size: 3.5rem; line-height: 1; }
.corkboard-lightbox-image {
  max-width: 160px;
  max-height: 160px;
  object-fit: contain;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.5));
}
.corkboard-lightbox-name {
  margin: var(--space-2) 0 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: var(--text-lg);
}
.corkboard-lightbox-desc {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.corkboard-lightbox-note {
  width: 100%;
  min-height: 120px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-4);
  border-radius: 2px;
  color: #2a2109;
  font-family: 'Caveat', cursive;
  font-size: 1.3rem;
  line-height: 1.3;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.45);
}
.corkboard-lightbox-empty { color: rgba(42, 33, 9, 0.55); font-family: var(--body); font-size: var(--text-sm); }

/* Playing Card lightbox face — same corner-free look as the pinned card
   itself (.corkboard-playing-card above), just larger, and reusing the
   exact same center content (js/lib/corkboard-engine.js's
   cardCenterContentHtml: Ace's big suit glyph, a 2-10's pip grid, or a
   J/Q/K's royal silhouette) — position:relative is what lets those
   absolutely-positioned pieces place themselves against THIS box instead of
   the lightbox card wrapper further up. .corkboard-playing-card-red/-black
   (defined above) already double as plain color rules, so they apply here
   unchanged; the size-specific overrides below just scale each piece up to
   this box's roughly 2x dimensions. */
.corkboard-lightbox-card-face {
  position: relative;
  width: 120px;
  height: 168px;
  background: var(--white);
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.corkboard-lightbox-card-face .corkboard-playing-card-suit { font-size: 4rem; line-height: 1; }
.corkboard-lightbox-card-face .corkboard-playing-card-pips { inset: 20px 14px; }
.corkboard-lightbox-card-face .corkboard-playing-card-pip { font-size: 1.15rem; }
.corkboard-lightbox-card-face .corkboard-playing-card-royal { width: 76px; height: 76px; }

/* ---------- View mode ---------- */
/* "Non-edit view" — presentation only. Drag/string/color/remove/align/font
   controls disappear, notes stop being editable, and tapping any pinned
   item opens the lightbox instead of moving it. The red tack itself stays
   visible here on purpose — stickers/stickies read as genuinely pinned to
   the board even when you can't touch anything (badges' tacks are already
   invisible always, via .corkboard-node-badge .corkboard-tack's own
   opacity:0, so this doesn't affect them). */
.corkboard-view-mode .corkboard-tray-wrap,
.corkboard-view-mode .corkboard-hint,
.corkboard-view-mode .corkboard-node-remove,
.corkboard-view-mode .corkboard-note-color-btn,
.corkboard-view-mode .corkboard-note-align-btn,
.corkboard-view-mode .corkboard-note-font-btn,
.corkboard-view-mode .corkboard-note-grip {
  display: none;
}
.corkboard-view-mode .corkboard-node { cursor: pointer; }
.corkboard-view-mode .corkboard-tack { cursor: pointer; }
.corkboard-view-mode .corkboard-note-card { cursor: pointer; }
.corkboard-view-mode .corkboard-strings path.corkboard-string-hit { pointer-events: none; }
