/* Mindmaps page (/dashboard/mindmaps). Tree logic: app/mindmaps.py. */

.mm-header {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-bottom: 14px; position: relative; flex-wrap: wrap;
}

.mm-menu-wrap { position: relative; }

.mm-switch-btn {
  display: flex; align-items: center; gap: 8px;
  background: var(--panel-2); border: 1px solid var(--border);
  padding: 8px 16px; color: var(--text); font-family: inherit;
  font-size: 12.5px; letter-spacing: 0.02em; text-transform: uppercase;
  cursor: pointer; max-width: 320px;
}
.mm-switch-btn:hover { border-color: var(--gold-dim); }
.mm-switch-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mm-switch-caret { color: var(--text-dim); font-size: 9px; flex: none; }

.mm-menu {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%); z-index: 5;
  margin-top: 6px; background: var(--panel); border: 1px solid var(--border);
  min-width: 160px; max-width: 240px; max-height: 320px; overflow-y: auto;
  display: flex; flex-direction: column;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  overscroll-behavior: contain;
}
.mm-menu-item {
  background: none; border: none; text-align: left; padding: 9px 14px;
  color: var(--text-dim); font-size: 11.5px; letter-spacing: 0.02em;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mm-menu-item:hover { background: var(--panel-2); color: var(--gold); }
.mm-menu-item.active { color: var(--gold); }
/* button.danger (myscape.css) outranks .mm-menu-item and sets width: 100%; reset
   so .danger only reddens the text on hover. */
.mm-menu-item.danger {
  width: auto;
  background: none;
  border: none;
  color: var(--red);
  padding: 9px 14px;
  font-size: 11.5px;
}
.mm-menu-item.danger:hover { color: var(--red); }
/* Armed "Are you sure?" state (closeMenu in the page script); held permanently,
   unlike :hover. */
.mm-menu-item--confirm,
.mm-menu-item--confirm:hover {
  color: var(--red);
  background: rgba(192, 90, 76, 0.12);
}
.mm-menu-divider { border-top: 1px solid var(--border); margin: 4px 0; }

.mm-menu-switch { text-align: center; }
#mmMenuNew, #mmMenuDuplicate, #mmMenuRename, #mmMenuDelete { text-align: center; text-transform: uppercase; }

.mm-name-form { display: flex; gap: 8px; justify-content: center; align-items: center; margin-bottom: 14px; }
.mm-name-form input[type="text"] { flex: 1 1 280px; max-width: 360px; }
/* Required-field miss is a red border only. The .mm-name-form + [type="text"]
   compound outranks myscape.css's input[type="text"] rule. */
.mm-name-form input[type="text"].invalid { border-color: var(--red); }
button.primary#mmNameSave, .mm-name-form-save {
  width: auto; flex: 0 0 auto;
  padding: 0 22px; height: 32px; margin-top: 0;
  font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
  display: inline-flex; align-items: center; justify-content: center;
}
.btn-secondary {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-dim); font-family: inherit;
  padding: 0 16px; height: 32px;
  font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.btn-secondary:hover { border-color: var(--gold-dim); color: var(--text); }
.btn-secondary:disabled { opacity: 0.5; cursor: default; }

/* Canvas */

/* Fixed-height frame so the tree pans/zooms without reflowing the page.
   touch-action: none keeps browser gestures from fighting drag panning. */
.mm-canvas {
  position: relative; overflow: hidden;
  height: 620px;
  background: var(--panel-translucent); border: 1px solid var(--border);
  cursor: grab; touch-action: none;
}
.mm-canvas.panning { cursor: grabbing; }

/* "Fill the window" mode (setFullscreen), not the Fullscreen API: escapes
   .wrap's 880px cap and .page padding. Height is overridden since the base is a
   fixed 620px. Opaque --panel so the covered header/footer do not bleed through. */
.mm-canvas.mm-fullscreen {
  position: fixed; inset: 0; z-index: 100;
  height: auto; border: none;
  background: var(--panel);
}

/* transform-origin: 0 0 so translate/scale compose from the top-left. Drag
   handlers zero transitionDuration so panning does not lag. */
.mm-world {
  position: absolute; top: 0; left: 0; transform-origin: 0 0;
  transition: transform 0.18s ease-out;
  /* No will-change: transform: a permanent layer is rasterized at a fixed scale,
     not the device pixel ratio, and stays blurry. */
}

.mm-lines { position: absolute; top: 0; left: 0; overflow: visible; pointer-events: none; }
.mm-lines path { stroke: var(--border); stroke-width: 1.5; fill: none; }

/* Undo/redo share the zoom group so the top-left stays free for
   click-to-deselect near a new root node. */
.mm-zoom-controls {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  display: flex; flex-direction: column; gap: 4px;
}
.mm-zoom-controls button {
  width: 28px; height: 28px; background: var(--panel-2); border: 1px solid var(--border);
  color: var(--text-dim); font-family: inherit; font-size: 14px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.mm-zoom-controls button:hover { border-color: var(--gold-dim); color: var(--text); }
.mm-zoom-divider { height: 1px; background: var(--border); margin: 2px 2px; }
.mm-zoom-controls button:disabled {
  opacity: 0.35; cursor: not-allowed; border-color: var(--border); color: var(--text-dim);
}
#mmZoomFit svg, #mmEditModeBtn svg, #mmUndoBtn svg, #mmRedoBtn svg, #mmFullscreenBtn svg { display: block; }

#mmEditModeBtn.active {
  border-color: var(--gold); color: var(--gold);
}
#mmEditModeBtn.active:hover { border-color: var(--gold); color: var(--gold); }

#mmFullscreenBtn.active {
  border-color: var(--gold); color: var(--gold);
}
#mmFullscreenBtn.active:hover { border-color: var(--gold); color: var(--gold); }

/* Hint pulse (glowEditModeButton) when a node is clicked with Edit Mode off; two
   iterations, so it reads as a nudge, not .active. */
@keyframes mm-edit-glow {
  0%, 100% { box-shadow: none; border-color: var(--border); color: var(--text-dim); }
  50% { box-shadow: 0 0 8px 2px var(--gold); border-color: var(--gold); color: var(--gold); }
}
#mmEditModeBtn.mm-glow { animation: mm-edit-glow 0.6s ease-in-out 2; }

/* Nodes
   Fixed width and height so the layout math needs no measurement pass; text
   clamps to 2 lines. */
.mm-node {
  position: absolute; box-sizing: border-box;
  width: 160px; height: 46px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  text-align: center; font-size: 11.5px; letter-spacing: 0.02em; text-transform: uppercase;
  line-height: 1.25; cursor: pointer; user-select: none;
}
.mm-view-mode .mm-node { cursor: default; }
.mm-node-label {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  width: 100%;
}
/* Restore selection (.mm-node is user-select: none): the root label is
   contenteditable and needs it for caret and select-all (renderCanvas). */
.mm-node-label[contenteditable="true"] {
  cursor: text; outline: none; user-select: text; -webkit-user-select: text;
}
.mm-node-label-with-icon {
  display: flex; align-items: center; justify-content: center; gap: 5px;
}
.mm-node-icon {
  width: 16px; height: 16px; flex: none; object-fit: contain;
  image-rendering: pixelated;
}
/* Manual-complete checkbox, subtler than the myscape.css base (it sits on the
   node face): black outline and 1.5px check, since the completed fill already
   says "done". Checked background is a step darker than --gray-bright to stand
   out from the fill. input.mm-node-checkbox matches the base specificity. */
.mm-node-checkbox { flex: none; }
input.mm-node-checkbox { background: #1b1b1c; }
input.mm-node-checkbox:checked { background: #55555a; border-color: #000; }
input.mm-node-checkbox:checked::after { border-color: #000; border-width: 0 1.5px 1.5px 0; }
.mm-node-label-text {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-width: 0;
}
.mm-node-x-lower { text-transform: lowercase; }

/* Note flip (renderCanvas/renderNoteFieldHtml)
   The flip runs on an inner wrapper, never .mm-node, whose box the layout math
   positions. Inert during Edit Mode (rule below). */
.mm-node-note-toggle {
  position: absolute; top: 2px; right: 2px; z-index: 1;
  width: 14px; height: 14px; padding: 0;
  background: none; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: #676768;
}
.mm-node-note-toggle svg { width: 12px; height: 12px; display: block; }
/* Inert in Edit Mode (#mmNodesLayer lacks .mm-view-mode); the click handler also
   checks MM.editMode. */
#mmNodesLayer:not(.mm-view-mode) .mm-node-note-toggle {
  pointer-events: none; cursor: default; opacity: 0.35;
}
/* Completed: icon a shade darker than the fill; gray uses the same ~78%
   lightness ratio. */
.mm-node-gray.completed .mm-node-note-toggle { color: #56565a; }
.mm-node-red.completed .mm-node-note-toggle { color: #96463b; }
.mm-node-orange.completed .mm-node-note-toggle { color: #9d5f30; }
.mm-node-yellow.completed .mm-node-note-toggle { color: #9d8c42; }
.mm-node-green.completed .mm-node-note-toggle { color: #578846; }
.mm-node-blue.completed .mm-node-note-toggle { color: #466d99; }
.mm-node-purple.completed .mm-node-note-toggle { color: #6c549d; }
.mm-node-white.completed .mm-node-note-toggle { color: #b8b3a8; }

/* Hard cut, no animation: flipNodeNote swaps faces in the DOM. rotateY
   (preserve-3d + backface-visibility) blurred text across the whole canvas via
   .mm-world. inset: 0 ignores padding, so the face re-applies 4px/10px. */
.mm-node-flip-inner { position: absolute; inset: 0; }
.mm-node-face {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 4px 10px; box-sizing: border-box;
}
.mm-node-face-back {
  text-transform: none; letter-spacing: normal; text-align: left;
  font-size: 9.5px; line-height: 1.35;
}
/* overflow: hidden, not auto: notes are capped to 2 lines (clampNoteInput). A
   scroller with overflowing content gets its own layer and forces .mm-world to
   re-rasterize (see .mm-suggestions). overscroll-behavior: contain stays for
   residual scrollTop. */
.mm-node-note-text { width: 100%; height: 100%; overflow: hidden; overscroll-behavior: contain; white-space: pre-wrap; word-break: break-word; }


/* Outlined until the server marks the goal complete (evaluate_completion in
   app/mindmaps.py; renderCanvas adds .completed), then filled. Hover brightens
   only background-color. Filled text is a dark same-hue tint (near-black for
   gray/white); outlined text is var(--text). Order matches NODE_COLORS in
   app/mindmaps.py; gold/amber are reserved (--orange/--yellow in myscape.css). */
.mm-node-gray { background: var(--panel-2); border-color: var(--gray-bright); color: var(--text); }
.mm-node-gray:hover { background-color: rgb(51, 51, 54); }
.mm-node-gray.completed { background: var(--gray-bright); border-color: var(--gray-bright); color: #1a1a1a; }
.mm-node-gray.completed:hover { background-color: #7f7f85; }

.mm-node-red { background: var(--panel-2); border-color: var(--red); color: var(--text); }
.mm-node-red:hover { border-color: #dd6857; }
.mm-node-red.completed { background: var(--red); border-color: var(--red); color: #1a0f0c; }
.mm-node-red.completed:hover { background-color: #dd6857; }

.mm-node-orange { background: var(--panel-2); border-color: var(--orange); color: var(--text); }
.mm-node-orange:hover { border-color: #e0924f; }
.mm-node-orange.completed { background: var(--orange); border-color: var(--orange); color: #1a1108; }
.mm-node-orange.completed:hover { background-color: #e0924f; }

.mm-node-yellow { background: var(--panel-2); border-color: var(--yellow); color: var(--text); }
.mm-node-yellow:hover { border-color: #ddc86a; }
.mm-node-yellow.completed { background: var(--yellow); border-color: var(--yellow); color: #1a1608; }
.mm-node-yellow.completed:hover { background-color: #ddc86a; }

.mm-node-green { background: var(--panel-2); border-color: var(--green); color: var(--text); }
.mm-node-green:hover { border-color: #80c868; }
.mm-node-green.completed { background: var(--green); border-color: var(--green); color: #0e150c; }
.mm-node-green.completed:hover { background-color: #80c868; }

.mm-node-blue { background: var(--panel-2); border-color: var(--blue); color: var(--text); }
.mm-node-blue:hover { border-color: #68a1e1; }
.mm-node-blue.completed { background: var(--blue); border-color: var(--blue); color: #0d121a; }
.mm-node-blue.completed:hover { background-color: #68a1e1; }

.mm-node-purple { background: var(--panel-2); border-color: var(--purple); color: var(--text); }
.mm-node-purple:hover { border-color: #a084d9; }
.mm-node-purple.completed { background: var(--purple); border-color: var(--purple); color: #140f1e; }
.mm-node-purple.completed:hover { background-color: #a084d9; }

.mm-node-white { background: var(--panel-2); border-color: var(--text); color: var(--text); }
.mm-node-white:hover { border-color: #ffffff; }
.mm-node-white.completed { background: var(--text); border-color: var(--text); color: var(--bg); }
.mm-node-white.completed:hover { background-color: #ffffff; }

/* Selection is the sticky hover brighten (rgb(51,51,54) lift, as
   .gear-slot/.loot-npc-box): each color's hover shade is reused as the selected
   border (outlined) or background (completed). */
.mm-node.selected { z-index: 2; background-color: rgb(51, 51, 54); }
.mm-node-red.selected { border-color: #dd6857; }
.mm-node-red.completed.selected { background-color: #dd6857; }
.mm-node-orange.selected { border-color: #e0924f; }
.mm-node-orange.completed.selected { background-color: #e0924f; }
.mm-node-yellow.selected { border-color: #ddc86a; }
.mm-node-yellow.completed.selected { background-color: #ddc86a; }
.mm-node-green.selected { border-color: #80c868; }
.mm-node-green.completed.selected { background-color: #80c868; }
.mm-node-blue.selected { border-color: #68a1e1; }
.mm-node-blue.completed.selected { background-color: #68a1e1; }
.mm-node-purple.selected { border-color: #a084d9; }
.mm-node-purple.completed.selected { background-color: #a084d9; }
.mm-node-white.selected { border-color: #ffffff; }
.mm-node-white.completed.selected { background-color: #ffffff; }

/* Per-node hover controls
   "+" buttons on a node's edges: top = parent, bottom = child, left/right =
   sibling. Children of .mm-node so they follow pan/zoom. */
.mm-edge-btn {
  position: absolute; z-index: 3;
  width: 14px; height: 14px; box-sizing: border-box;
  background: var(--panel-2); border: 1px solid var(--gold-dim); color: var(--gold);
  font-family: inherit; font-size: 11px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
  opacity: 0; pointer-events: none;
}
/* No transition: one on anything inside .mm-world makes Chrome promote the
   element to its own layer and repaint the shared one, blurring other nodes on
   every hover. */
/* Hide while hovering the popover, which is a DOM descendant so .mm-node:hover
   stays matched. */
.mm-node:hover:not(:has(.mm-node-popover:hover)) .mm-edge-btn { opacity: 1; pointer-events: auto; }
/* While a popover is open, other nodes' edge buttons must not show:
   .mm-edge-btn's z-index: 3 leaks into the shared stacking context and beats the
   popover node's z-index: 2. The #id raises specificity instead of z-index. */
#mmNodesLayer:has(.mm-node-popover) .mm-node:not(:has(> .mm-node-popover)) .mm-edge-btn {
  opacity: 0; pointer-events: none;
}
.mm-edge-btn:hover { background: var(--gold); color: #1a1408; border-color: var(--gold); }
.mm-edge-top { top: -7px; left: 50%; transform: translateX(-50%); }
.mm-edge-bottom { bottom: -7px; left: 50%; transform: translateX(-50%); }
.mm-edge-left { left: -7px; top: 50%; transform: translateY(-50%); }
.mm-edge-right { right: -7px; top: 50%; transform: translateY(-50%); }

/* Per-node popover
   Below the anchor node (MM.selectedNodeId). Offset past the bottom edge
   button's 7px reach (.mm-edge-bottom) so it stays clickable under this higher
   z-index. */
.mm-node-popover {
  position: absolute; z-index: 4; top: calc(100% + 22px); left: 50%; transform: translateX(-50%);
  background: var(--panel); border: 1px solid var(--border); padding: 10px;
  display: flex; flex-direction: column; gap: 10px; align-items: center;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  cursor: default; /* .mm-node itself is cursor: pointer -- this panel isn't the node */
}
.mm-node-popover-wide { width: 250px; }

/* Goal-type picker (renderGoalTypePickerHtml)
   3-column grid in GOAL_TYPE_PICKER order (KC/ITEM/GP, SKILL/QUEST/DIARY,
   MANUAL); a click sends only a `color`. */
.mm-type-picker {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; width: 100%;
}
.mm-type-btn {
  display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 5px;
  background: var(--panel-2); border: 1px solid var(--border); padding: 6px 6px;
  color: var(--text-dim); font-family: inherit;
  font-size: 9.5px; letter-spacing: 0.02em; cursor: pointer;
}
.mm-type-btn:hover { border-color: var(--gold-dim); color: var(--text); }
.mm-type-btn.active { border-color: var(--gold); color: var(--gold); }
.mm-type-btn-icon {
  width: 18px; height: 18px; object-fit: contain; flex: none;
  image-rendering: pixelated;
}
/* Static always-checked copy of the node checkbox
   (input.mm-node-checkbox:checked), scaled to the 18x18 icon slot. */
.mm-type-btn-swatch {
  position: relative; width: 18px; height: 18px; flex: none; background: var(--panel-2); border: 1px solid var(--border);
}
.mm-type-btn-swatch-manual { background: #55555a; border-color: #000; }
.mm-type-btn-swatch-manual::after {
  content: ''; position: absolute; left: 5px; top: 2px;
  width: 5px; height: 9px;
  border: solid #000; border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.mm-type-btn-label { text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.mm-popover-actions {
  display: flex; flex-direction: row; gap: 8px; width: 100%; justify-content: center;
}
.mm-popover-actions .mm-goal-save-btn,
.mm-popover-actions .mm-color-popover-delete {
  flex: 1 1 0; width: auto; height: 25px;
}
.mm-popover-actions:has(.mm-color-popover-delete:only-child) .mm-color-popover-delete {
  flex: 0 1 auto; padding: 0 20px;
}

/* Primary action, as button.primary/.gt-add-btn. Dark text: white on --gold
   (#ff981f) reads poorly. */
.mm-goal-save-btn {
  background: var(--gold); border-color: var(--gold); color: #221c10;
}
.mm-goal-save-btn:hover { background: #ffab42; border-color: #ffab42; color: #221c10; }

.mm-color-popover-delete { width: auto; height: 25px; align-self: center; }
.mm-delete-btn:hover { border-color: var(--red); color: var(--red); }
.mm-delete-btn:disabled:hover { border-color: var(--border); color: var(--text-dim); }

.mm-add-shared-child-btn { width: auto; height: 22px; align-self: center; white-space: nowrap; }

/* Goal-criteria form (renderGoalFieldsHtml)
   Type-specific fields, or a gray node's name box, in the Edit-Mode popover.
   Save is in .mm-popover-actions. */
.mm-goal-fields {
  display: flex; flex-direction: column; gap: 8px; width: 100%; align-items: stretch;
}
.mm-goal-fields input.mm-goal-input {
  width: 100%; box-sizing: border-box;
  background: #100e0b; border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text); font-family: inherit; font-size: 11.5px;
  padding: 7px 8px; height: 30px;
}
.mm-goal-fields input.mm-goal-input:focus { outline: none; border-color: var(--gold-dim); }
.mm-goal-fields input.mm-goal-input::placeholder { color: var(--text-faint); }
/* Invalid: red border only, no message (validateGoalFieldsForSave,
   validateSkillTarget, validateNodeText). One ancestor rule covers every nested
   input.mm-goal-input. */
.mm-goal-fields input.mm-goal-input.invalid { border-color: var(--red); }

.mm-search-wrap { width: 100%; }
/* Not a child of .mm-search-wrap: one shared element on <body>, `position:
   fixed`, tracks the focused input (getFloatingSuggestionsEl). A scrollable list
   inside .mm-world is promoted to its own GPU layer, forcing .mm-world to
   re-rasterize and blur node text; `contain: paint` and will-change made it
   worse. */
.mm-suggestions {
  display: none; position: fixed; z-index: 1000;
  background: var(--panel); border: 1px solid var(--border); max-height: 180px; overflow-y: auto;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45);
  /* The canvas wheel handler ignores popover targets; contain stops chaining
     into the document. */
  overscroll-behavior: contain;
}
.mm-suggestions.show { display: block; }
.mm-suggestion {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 11.5px;
  color: var(--text); cursor: pointer; text-transform: none;
}
.mm-suggestion:hover { background: var(--panel-2); }
.mm-suggestion img {
  width: 20px; height: 20px; object-fit: contain; image-rendering: pixelated; flex: none;
}
/* Item/diary icons are RuneLite CDN images with right-hand padding for the
   stack-count badge; translate recenters (see .loot-item-box-icon in
   loot-tracker.css). */
.mm-suggestion[data-kind="item"] img,
.mm-suggestion[data-kind="diary"] img {
  transform: translate(5.5%, -1%);
}
.mm-suggestion-empty {
  padding: 6px 8px; font-size: 11.5px; color: var(--text-faint); font-style: italic; cursor: default;
  text-transform: none;
}

/* Segmented toggle (renderSegmentedToggleHtml)
   Equal-width buttons replacing a <select>, backed by a hidden input. */
.mm-segment-group {
  display: flex; flex-direction: row; width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
}
/* A <button>'s UA styles reset text-transform, so uppercase is explicit. */
.mm-segment-btn {
  flex: 1 1 0; height: 25px; background: #100e0b; border: none; border-right: 1px solid var(--border);
  color: var(--text-dim); font-family: inherit; font-size: 10.5px; letter-spacing: 0.02em;
  padding: 0 4px; cursor: pointer; text-align: center; text-transform: uppercase;
  display: flex; align-items: center; justify-content: center;
}
.mm-segment-btn:last-child { border-right: none; }
.mm-segment-btn:hover { color: var(--text); }
.mm-segment-btn.active { background: var(--panel-2); color: var(--gold); }

.mm-goal-skill-row { display: flex; flex-direction: row; gap: 6px; width: 100%; align-items: center; }
.mm-goal-skill-row .mm-search-wrap { flex: 1 1 auto; width: auto; min-width: 0; }
.mm-goal-skill-row .mm-segment-group { flex: 0 0 auto; width: auto; }
.mm-goal-skill-row .mm-segment-btn { flex: 0 0 auto; padding: 0 10px; }

/* Inline field validation (validateSkillTarget) */
.mm-goal-target-wrap { display: flex; flex-direction: column; gap: 4px; width: 100%; }
.mm-field-error { display: none; font-size: 10.5px; color: var(--red); line-height: 1.3; }
.mm-field-error.show { display: block; }

/* KC goal's optional sinceDate field (renderGoalFieldsHtml)
   Masked date input as .loot-range-custom-field (loot-tracker.css).
   color-scheme: dark darkens its caret/selection UI. */
.mm-goal-date-wrap { display: flex; flex-direction: column; gap: 4px; width: 100%; }
.mm-goal-date-wrap input.mm-goal-input { color-scheme: dark; }
.mm-goal-hint { font-size: 10.5px; color: var(--text-faint); line-height: 1.3; }

.mm-note-wrap { width: 100%; }
.mm-note-wrap textarea.mm-goal-input {
  width: 100%; box-sizing: border-box;
  background: #100e0b; border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text); font-family: inherit; font-size: 11.5px;
  padding: 7px 8px;
  text-transform: none; letter-spacing: normal;
  /* Not resizable or scrollable: notes are capped to 2 lines (clampNoteInput),
     so overflow: hidden only backstops the scroller blur (see .mm-suggestions).
     overscroll-behavior: contain stays: clampNoteInput measures against the
     node's back face, leaving a few px of scrollTop that would chain to the
     page. */
  overflow: hidden; overscroll-behavior: contain;
}
.mm-note-wrap textarea.mm-goal-input:focus { outline: none; border-color: var(--gold-dim); }
.mm-note-wrap textarea.mm-goal-input::placeholder { color: var(--text-faint); }

/* Zero-mindmaps state */
.mm-empty-wrap { text-align: center; padding: 40px 10px; }
.mm-empty { color: var(--text-faint); font-size: 13px; margin: 0 0 16px; }
/* button.primary defaults to width: 100% (see #pgNameSave/#mmNameSave above) and
   lacks all-caps. */
button.primary#mmCreateFirstBtn {
  width: auto; padding: 0 22px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
}

/* Landing page preview (render_landing_mindmap_preview, app/pages/)
   Same container as .landing-loot-preview (loot-tracker.css) and
   .landing-purchase-preview; see the former for the feather reasoning. */
.landing-mindmap-preview {
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  height: calc(100vh - 80px);
  height: calc(100svh - 80px);
  overflow: hidden;
  position: relative;
}
.landing-mindmap-preview::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 15%;
  background: linear-gradient(to bottom, transparent, var(--bg));
  pointer-events: none;
}

/* Hidden, not stripped, so _mindmaps_panel_markup is untouched; the preview has
   one mindmap. */
.landing-mindmap-preview .page-title,
.landing-mindmap-preview .mm-header {
  display: none;
}

/* Landing-preview hero row
   Mirrors .loot-preview-hero (loot-tracker.css) with the tagline first, the
   reverse of Purchase Goals. */
.mindmap-preview-hero {
  display: flex;
  align-items: flex-end;
}
.mindmap-preview-tagline-wrap {
  flex: 1 1 auto;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}
.mindmap-preview-tagline {
  margin: 0;
  max-width: 100%;
  min-width: 0;
  text-align: center;
  font-size: 30px;
  line-height: 1.25;
  font-weight: 400;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mindmap-preview-tagline-gold { color: var(--gold); }
.mindmap-preview-tagline-white { color: var(--text); }

.mindmap-preview-char {
  flex: 0 0 auto;
  height: 157px;
  line-height: 0;
}
.landing-mindmap-preview .mindmap-preview-char img {
  display: block;
  height: 100%;
  max-height: none;
  width: auto;
  max-width: none;
}


/* Share icon beside the page title
   Same recipe as .loot-share-wrap/.pg-share-wrap/.pm-share-wrap; the 32px top
   and centering are measured there (.loot-share-card in loot-tracker.css) and
   carry over since h2.page-title has the same 22px/1.5 font. */
.mm-share-wrap {
  position: relative; display: inline-block; vertical-align: middle; margin-left: 8px;
}
.mm-share-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  line-height: 1;
  color: var(--text-faint);
  transition: color 0.15s ease;
  position: relative;
  top: -2px;
}
.mm-share-icon-btn:hover { color: var(--text-dim); }
.mm-share-icon-btn.open { color: var(--gold); }
.mm-share-card {
  position: absolute; top: 32px; left: 50%; transform: translateX(-50%); z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 280px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 5px 6px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}
/* Author `display` outranks the UA [hidden] rule (as .loot-share-card[hidden]). */
.mm-share-card[hidden] { display: none; }
.mm-share-generate-btn {
  height: 28px;
  padding: 0 10px;
  font-size: 15px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--text-dim);
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  cursor: pointer;
}
.mm-share-generate-btn:hover { border-color: var(--gold-dim); color: var(--text); }
.mm-share-generate-btn:disabled { cursor: default; opacity: 0.6; }
.mm-share-generate-btn.action-failed { border-color: var(--red); color: var(--red); }
.mm-share-link-btn {
  height: 28px;
  padding: 0 10px;
  font-size: 15px;
  font-family: inherit;
  color: var(--text-dim);
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mm-share-link-btn:hover { border-color: var(--gold-dim); color: var(--text); }
.mm-share-popup-hint { margin: 0; font-size: 13px; color: var(--text-faint); text-align: center; }

/* Save-to-your-account icon (import_icon_btn, render_mindmap_share in
   app/pages/)
   Same recipe as .pg-import-wrap/.pg-import-card (purchase-goals.css). */
.mm-import-wrap { position: relative; display: inline-block; vertical-align: middle; margin-left: 8px; }
.mm-import-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0; margin: 0;
  cursor: pointer; line-height: 1;
  color: var(--text-faint); transition: color 0.15s ease;
  position: relative;
  top: -2px;
}
.mm-import-icon-btn:hover { color: var(--text-dim); }
.mm-import-icon-btn.open { color: var(--gold); }
.mm-import-card {
  position: absolute; top: 32px; left: 50%; transform: translateX(-50%); z-index: 5;
  display: flex; flex-direction: column; gap: 6px; width: 280px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 5px 6px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}
.mm-import-card[hidden] { display: none; }
.mm-import-save-btn {
  height: 28px; padding: 0 10px;
  font-size: 15px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--text-dim);
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  cursor: pointer;
}
.mm-import-save-btn:hover { border-color: var(--gold-dim); color: var(--text); }
.mm-import-save-btn:disabled { cursor: default; opacity: 0.6; }
.mm-import-save-btn.action-failed { border-color: var(--red); color: var(--red); }
.mm-import-popup-hint { margin: 0; font-size: 13px; color: var(--text-faint); text-align: center; }

/* Share-viewing page (myscape.io/s/<code>, render_mindmap_share in app/pages/) */

.mm-share-label { text-align: center; margin: 0 0 4px; }

.mindmap-share .mm-header { display: none; }

/* Undo/Redo, Edit Mode and both .mm-zoom-divider seams are hidden too (no
   orphaned seam); zoom and fullscreen mutate nothing and stay. */
.mindmap-share #mmUndoBtn,
.mindmap-share #mmRedoBtn,
.mindmap-share #mmEditModeBtn { display: none; }
.mindmap-share .mm-zoom-controls .mm-zoom-divider:first-of-type,
.mindmap-share .mm-zoom-controls .mm-zoom-divider:last-of-type { display: none; }

/* The remaining live interaction, a gray node's manual-toggle checkbox, is
   locked with pointer-events: none. Edit Mode never turns on here (localStorage
   shadow, see render_mindmap_share). */
.mindmap-share .mm-node-checkbox[data-role="manual-toggle"] { pointer-events: none; }


/* Phone
   320px centered column with a side gutter for thumb page-scroll (the canvas is
   touch-action: none). Fullscreen opts back out; body:not(.phone-off) leaves the
   homepage preview alone. */
@media (max-width: 700px) {
  body:not(.phone-off) .mm-header { margin-bottom: 12px; }
  body:not(.phone-off) .mm-switch-btn { max-width: 320px; }
  body:not(.phone-off) .mm-name-form { flex-wrap: wrap; max-width: 320px; margin-left: auto; margin-right: auto; }
  body:not(.phone-off) .mm-name-form input[type="text"] { flex: 1 1 100%; max-width: none; }
  body:not(.phone-off) .mm-canvas {
    width: 100%; max-width: 320px; margin-left: auto; margin-right: auto;
    height: 480px;
  }
  body:not(.phone-off) .mm-canvas.mm-fullscreen {
    max-width: none; width: auto; height: auto; margin: 0;
  }
  /* Controls run along the top edge (a right-side column covered the map) with
     vertical dividers. The row ignores touches so a drag beside it still pans. */
  body:not(.phone-off) .mm-zoom-controls {
    top: 8px; left: 8px; right: 8px;
    flex-direction: row; align-items: center; justify-content: center; gap: 4px;
    pointer-events: none;
  }
  body:not(.phone-off) .mm-zoom-controls button { width: 34px; height: 34px; pointer-events: auto; }
  body:not(.phone-off) .mm-zoom-controls .mm-zoom-divider {
    width: 1px; height: auto; align-self: stretch; margin: 2px 2px;
  }

  body:not(.phone-off) .page-title:has(.mm-share-wrap) { position: relative; }
  body:not(.phone-off) .mm-share-wrap { position: static; }
  body:not(.phone-off) .mm-share-card { top: calc(100% + 6px); width: min(280px, 100%); box-sizing: border-box; }
  body:not(.phone-off) .mindmap-share .mm-zoom-controls { justify-content: center; }
  body:not(.phone-off) .mm-canvas.mm-fullscreen .mm-zoom-controls {
    top: max(8px, env(safe-area-inset-top));
    left: max(8px, env(safe-area-inset-left));
    right: max(8px, env(safe-area-inset-right));
  }
}


/* Homepage preview on touch: finger drags scroll the page (pan-y) until
   .mm-fullscreen. */
.landing-mindmap-preview .mm-canvas:not(.mm-fullscreen) {
  touch-action: pan-y;
}


/* Share page save icon, phone: anchor the 280px popup to the full-width title
   (centered on the icon it ran off the right edge). Thumb-sized tap area; the
   negative margin keeps layout fixed. */
@media (max-width: 700px) {
  body:not(.phone-off) .page-title:has(.mm-import-wrap) { position: relative; }
  body:not(.phone-off) .mm-import-wrap { position: static; }
  body:not(.phone-off) .mm-import-card { top: calc(100% + 6px); }
  body:not(.phone-off) .mm-import-icon-btn { padding: 10px; margin: -10px; }
}

