* {
  box-sizing: border-box;
}

html,
body,
#app-shell {
  height: 100%;
  margin: 0;
}

body {
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  color: #1f2933;
}

#app-shell {
  display: flex;
  flex-direction: column;
}

.app-header {
  padding: 10px 16px;
  border-bottom: 1px solid #d0d3da;
  background: #111827;
  color: #f9fafb;
}

.app-header h1 {
  margin: 0 0 4px;
  font-size: 15px;
}

.app-header p {
  margin: 0;
  font-size: 12px;
  color: #9ca3af;
}

/* ADR-0020/0024/0025: 계측 대상 앱은 좌우 분할의 한쪽이 아니라 화면 전체를 그대로 쓴다 —
   보드는 BoardOverlay.tsx가 별도 root(#board-overlay-root, document.body 직속)로 얹는
   플로팅 버튼 + 하단 도킹 패널(ADR-0025)이라 여기서 자리를 안 잡아줘도 된다. */
.subject-root {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 16px;
}

/* ADR-0040(오버레이 전용): 보드 패널은 계측 대상 앱 위에 떠서 덮기만 하고, 앱 레이아웃/CSS는
   절대 건드리지 않는다. 예전엔 패널 크기만큼 .subject-root에 패딩을 넣어 앱을 밀어냈는데,
   이는 "관찰 도구가 관찰 대상의 반응형/레이아웃을 바꾼다"는 문제라 제거했다 — 가려진 부분은
   패널을 이동/크기조절/닫아서 본다(react-scan/TanStack Query Devtools와 같은 모델). */

.demo-app {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.demo-app section,
.demo-app > div {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px;
}

.demo-app h2 {
  margin-top: 0;
  font-size: 13px;
  text-transform: uppercase;
  color: #6b7280;
}

.demo-button {
  border: 1px solid #6366f1;
  background: #eef2ff;
  color: #312e81;
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
  margin: 2px 4px 2px 0;
}

.demo-button--ghost {
  background: transparent;
  border-style: dashed;
}

.live-feed__bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 40px;
  margin: 8px 0;
}

.live-feed__bar {
  width: 4px;
  background: #6366f1;
  border-radius: 1px;
}

.store-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.store-header__logo {
  font-weight: 700;
  font-size: 14px;
}

.category-filter-bar {
  margin-bottom: 10px;
}

.product-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
}

.product-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  padding: 6px;
}

.product-card__image {
  height: 56px;
  border-radius: 4px;
  background: #e0e7ff;
}

.product-card__image--accessories {
  background: #fce7f3;
}

.product-card__image--footwear {
  background: #dcfce7;
}

.product-card__info {
  display: flex;
  flex-direction: column;
  font-size: 12px;
}

.product-card__price {
  color: #6b7280;
}

.cart-drawer {
  margin-top: 10px;
  border-top: 1px dashed #d1d5db;
  padding-top: 8px;
}

.cart-drawer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.cart-line-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  padding: 2px 0;
}

.cart-line-item__name {
  flex: 1;
}

.cart-line-item__remove {
  border: none;
  background: transparent;
  color: #9ca3af;
  cursor: pointer;
}

.cart-drawer__total {
  margin-top: 6px;
  font-weight: 600;
  font-size: 12px;
}


/* --- /shop 라우트 v2: 통제된 기능 커버리지 데모 쇼핑몰(ADR-0057 확장). 항상 마운트된 데모
   패널·Storefront와 분리된, 실제 URL 전환에 반응하는 독립 페이지. --- */

.shop-site-v2 {
  background: #fff;
  min-height: 100%;
  color: #1f2933;
}

/* 헤더 */
.site-header-v2 {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: #fff;
  border-bottom: 1px solid #e5e7eb;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.site-header-v2__logo {
  border: none;
  background: transparent;
  font-weight: 800;
  font-size: 18px;
  color: #ff6f0f;
  cursor: pointer;
}
.header-search {
  flex: 1;
}
.header-search__input {
  width: 100%;
  border: 1px solid #d1d5db;
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 13px;
}
.site-header-v2__back {
  border: none;
  background: transparent;
  color: #6b7280;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}

/* 프로모 */
.promo-carousel {
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(90deg, #ff6f0f, #ff9a5c);
  color: #fff;
  font-weight: 600;
  font-size: 13px;
  padding: 10px 16px;
}
.countdown-timer {
  font-variant-numeric: tabular-nums;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 4px;
  padding: 2px 8px;
  font-weight: 700;
}
.promo-carousel__dots {
  margin-left: auto;
  display: flex;
  gap: 4px;
}
.promo-carousel__dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.5);
}
.promo-carousel__dot--on {
  background: #fff;
}

/* 카테고리 탭 + 툴바 */
.category-tabs {
  display: flex;
  gap: 8px;
  padding: 10px 16px;
  overflow-x: auto;
  border-bottom: 1px solid #f0f0f0;
}
.category-tabs__item {
  border: 1px solid #d1d5db;
  background: #fff;
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}
.category-tabs__item--active {
  border-color: #ff6f0f;
  background: #fff1e6;
  color: #ff6f0f;
  font-weight: 600;
}
.product-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  font-size: 12px;
  color: #6b7280;
}
.product-toolbar__sort select {
  margin-left: 6px;
  padding: 4px 8px;
  border-radius: 6px;
  border: 1px solid #d1d5db;
}

/* 상품 섹션/그리드/카드 */
.product-section-v2 {
  padding: 12px 16px;
}
.product-section-v2 h2 {
  font-size: 15px;
  margin: 0 0 10px;
  padding-left: 8px;
  border-left: 3px solid #ff6f0f;
}
.product-section-v2__count {
  font-size: 12px;
  color: #9ca3af;
  font-weight: 400;
}
.product-grid-v2 {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.product-card-v2 {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  border: 1px solid #f0f0f0;
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.product-card-v2__thumb-btn {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
}
.product-thumb {
  position: relative;
  height: 96px;
  border-radius: 6px;
  background: #e0e7ff;
}
.product-thumb--accessories {
  background: #fce7f3;
}
.product-thumb--footwear {
  background: #dcfce7;
}
.product-thumb--living {
  background: #fef3c7;
}
.product-thumb__badge {
  position: absolute;
  top: 6px;
  left: 6px;
  font-size: 10px;
  font-weight: 700;
  border-radius: 4px;
  padding: 2px 6px;
  color: #fff;
}
.product-thumb__badge--rocket {
  background: #3b82f6;
}
.product-thumb__badge--free {
  background: #10b981;
}
.wishlist-button {
  position: absolute;
  top: 12px;
  right: 12px;
  border: none;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 999px;
  width: 26px;
  height: 26px;
  cursor: pointer;
  color: #9ca3af;
  font-size: 14px;
}
.wishlist-button--on {
  color: #ef4444;
}
.product-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12px;
}
.product-meta__name {
  font-weight: 500;
}
.price-block__row {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.discount-badge {
  color: #ff6f0f;
  font-weight: 700;
  font-size: 13px;
}
.price-block__final {
  font-weight: 700;
  font-size: 14px;
}
.price-block__original {
  font-size: 11px;
  color: #9ca3af;
  text-decoration: line-through;
}
.star-rating {
  font-size: 11px;
  color: #f59e0b;
  display: inline-flex;
  gap: 3px;
  align-items: center;
}
.star-rating__count {
  color: #9ca3af;
}
.product-card-v2__actions {
  display: flex;
  gap: 4px;
}

/* 퀵뷰 모달(포탈) */
.qv-overlay,
.cart-drawer-v2-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  z-index: 2147483000;
}
.qv-overlay {
  align-items: center;
  justify-content: center;
}
.qv-modal {
  position: relative;
  display: flex;
  gap: 16px;
  background: #fff;
  border-radius: 12px;
  padding: 20px;
  max-width: 560px;
  width: 90%;
}
.qv-modal__close,
.cart-drawer-v2__head button {
  position: absolute;
  top: 10px;
  right: 12px;
  border: none;
  background: transparent;
  font-size: 20px;
  cursor: pointer;
  color: #9ca3af;
}
.qv-gallery {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  width: 160px;
  flex-shrink: 0;
}
.qv-gallery__thumb {
  height: 76px;
  border-radius: 6px;
  background: #e0e7ff;
}
.qv-gallery__thumb--accessories {
  background: #fce7f3;
}
.qv-gallery__thumb--footwear {
  background: #dcfce7;
}
.qv-gallery__thumb--living {
  background: #fef3c7;
}
.qv-modal__info {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.qv-modal__info h3 {
  margin: 0;
}
.qv-modal__stock {
  font-size: 12px;
  color: #6b7280;
  margin: 0;
}

/* 장바구니 드로어(포탈) */
.cart-drawer-v2-overlay {
  justify-content: flex-end;
}
.cart-drawer-v2 {
  position: relative;
  width: 360px;
  max-width: 90%;
  background: #fff;
  height: 100%;
  padding: 16px;
  overflow-y: auto;
  box-shadow: -2px 0 12px rgba(0, 0, 0, 0.15);
}
.cart-drawer-v2__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.cart-drawer-v2__head h3 {
  margin: 0;
}
.cart-drawer-v2__head button {
  position: static;
}
.cart-drawer-v2__empty {
  color: #9ca3af;
  font-size: 13px;
}
.cart-drawer-v2__list {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
}
.cart-line-v2 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  padding: 6px 0;
  border-bottom: 1px solid #f3f4f6;
}
.cart-line-v2__name {
  flex: 1;
}
.cart-line-v2__remove {
  border: none;
  background: transparent;
  color: #9ca3af;
  cursor: pointer;
}
.qty-stepper {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.qty-stepper__value {
  min-width: 20px;
  text-align: center;
}
.cart-summary {
  border-top: 1px dashed #d1d5db;
  padding-top: 8px;
  font-size: 13px;
}
.cart-summary__row {
  display: flex;
  justify-content: space-between;
  padding: 3px 0;
}
.cart-summary__row--accent {
  font-weight: 700;
  color: #ff6f0f;
  border-top: 1px solid #f3f4f6;
  margin-top: 4px;
  padding-top: 6px;
}
.checkout-panel-v2 {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
}
.checkout-fallback {
  margin-top: 10px;
  padding: 10px;
  border: 1px dashed #ef4444;
  border-radius: 8px;
  font-size: 12px;
  color: #b91c1c;
}
.checkout-fallback button {
  margin-top: 6px;
}

/* 리뷰(lazy/Suspense) */
.reviews-section {
  padding: 12px 16px;
}
.reviews-section h2 {
  font-size: 15px;
  margin: 0 0 10px;
}
.review-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.review-card {
  border: 1px solid #f0f0f0;
  border-radius: 8px;
  padding: 8px 10px;
}
.review-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.review-card__author {
  font-weight: 600;
  font-size: 13px;
}
.review-card__text {
  margin: 4px 0 0;
  font-size: 12px;
  color: #4b5563;
}
.reviews-section__loading {
  color: #9ca3af;
  font-size: 13px;
}

/* 푸터 */
.site-footer-v2 {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  padding: 24px 16px;
  margin-top: 16px;
  border-top: 1px solid #e5e7eb;
  background: #fafafa;
}
.site-footer-v2__col h4 {
  margin: 0 0 8px;
  font-size: 13px;
}
.site-footer-v2__col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 12px;
  color: #6b7280;
}
.site-footer-v2__col li {
  padding: 2px 0;
}
.site-footer-v2__copy {
  flex-basis: 100%;
  font-size: 11px;
  color: #9ca3af;
  margin: 0;
}
/* this gets exported as style.css and can be used for the default theming */
/* these are the necessary styles for React/Svelte Flow, they get used by base.css and style.css */
.react-flow {
  direction: ltr;

  --xy-edge-stroke-default: #b1b1b7;
  --xy-edge-stroke-width-default: 1;
  --xy-edge-stroke-selected-default: #555;

  --xy-connectionline-stroke-default: #b1b1b7;
  --xy-connectionline-stroke-width-default: 1;

  --xy-attribution-background-color-default: rgba(255, 255, 255, 0.5);

  --xy-minimap-background-color-default: #fff;
  --xy-minimap-mask-background-color-default: rgba(240, 240, 240, 0.6);
  --xy-minimap-mask-stroke-color-default: transparent;
  --xy-minimap-mask-stroke-width-default: 1;
  --xy-minimap-node-background-color-default: #e2e2e2;
  --xy-minimap-node-stroke-color-default: transparent;
  --xy-minimap-node-stroke-width-default: 2;

  --xy-background-color-default: transparent;
  --xy-background-pattern-dots-color-default: #91919a;
  --xy-background-pattern-lines-color-default: #eee;
  --xy-background-pattern-cross-color-default: #e2e2e2;
  background-color: var(--xy-background-color, var(--xy-background-color-default));
  --xy-node-color-default: inherit;
  --xy-node-border-default: 1px solid #1a192b;
  --xy-node-background-color-default: #fff;
  --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25);
  --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, 0.08);
  --xy-node-boxshadow-selected-default: 0 0 0 0.5px #1a192b;
  --xy-node-border-radius-default: 3px;

  --xy-handle-background-color-default: #1a192b;
  --xy-handle-border-color-default: #fff;

  --xy-selection-background-color-default: rgba(0, 89, 220, 0.08);
  --xy-selection-border-default: 1px dotted rgba(0, 89, 220, 0.8);

  --xy-controls-button-background-color-default: #fefefe;
  --xy-controls-button-background-color-hover-default: #f4f4f4;
  --xy-controls-button-color-default: inherit;
  --xy-controls-button-color-hover-default: inherit;
  --xy-controls-button-border-color-default: #eee;
  --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);

  --xy-edge-label-background-color-default: #ffffff;
  --xy-edge-label-color-default: inherit;
  --xy-resize-background-color-default: #3367d9;
}
.react-flow.dark {
  --xy-edge-stroke-default: #3e3e3e;
  --xy-edge-stroke-width-default: 1;
  --xy-edge-stroke-selected-default: #727272;

  --xy-connectionline-stroke-default: #b1b1b7;
  --xy-connectionline-stroke-width-default: 1;

  --xy-attribution-background-color-default: rgba(150, 150, 150, 0.25);

  --xy-minimap-background-color-default: #141414;
  --xy-minimap-mask-background-color-default: rgba(60, 60, 60, 0.6);
  --xy-minimap-mask-stroke-color-default: transparent;
  --xy-minimap-mask-stroke-width-default: 1;
  --xy-minimap-node-background-color-default: #2b2b2b;
  --xy-minimap-node-stroke-color-default: transparent;
  --xy-minimap-node-stroke-width-default: 2;

  --xy-background-color-default: #141414;
  --xy-background-pattern-dots-color-default: #555;
  --xy-background-pattern-lines-color-default: #333;
  --xy-background-pattern-cross-color-default: #333;
  --xy-node-color-default: #f8f8f8;
  --xy-node-border-default: 1px solid #3c3c3c;
  --xy-node-background-color-default: #1e1e1e;
  --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25);
  --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, 0.08);
  --xy-node-boxshadow-selected-default: 0 0 0 0.5px #999;

  --xy-handle-background-color-default: #bebebe;
  --xy-handle-border-color-default: #1e1e1e;

  --xy-selection-background-color-default: rgba(200, 200, 220, 0.08);
  --xy-selection-border-default: 1px dotted rgba(200, 200, 220, 0.8);

  --xy-controls-button-background-color-default: #2b2b2b;
  --xy-controls-button-background-color-hover-default: #3e3e3e;
  --xy-controls-button-color-default: #f8f8f8;
  --xy-controls-button-color-hover-default: #fff;
  --xy-controls-button-border-color-default: #5b5b5b;
  --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);

  --xy-edge-label-background-color-default: #141414;
  --xy-edge-label-color-default: #f8f8f8;
}
.react-flow__background {
  background-color: var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));
  pointer-events: none;
  z-index: -1;
}
.react-flow__container {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
}
.react-flow__pane {
  z-index: 1;
  touch-action: none;
}
.react-flow__pane.draggable {
    cursor: grab;
  }
.react-flow__pane.dragging {
    cursor: grabbing;
  }
.react-flow__pane.selection {
    cursor: pointer;
  }
.react-flow__viewport {
  transform-origin: 0 0;
  z-index: 2;
  pointer-events: none;
}
.react-flow__renderer {
  z-index: 4;
}
.react-flow__selection {
  z-index: 6;
}
.react-flow__nodesselection-rect:focus,
.react-flow__nodesselection-rect:focus-visible {
  outline: none;
}
.react-flow__edge-path {
  stroke: var(--xy-edge-stroke, var(--xy-edge-stroke-default));
  stroke-width: var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));
  fill: none;
}
.react-flow__connection-path {
  stroke: var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));
  stroke-width: var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));
  fill: none;
}
.react-flow .react-flow__edges {
  position: absolute;
}
.react-flow .react-flow__edges svg {
    overflow: visible;
    position: absolute;
    pointer-events: none;
  }
.react-flow__edge {
  pointer-events: visibleStroke;
}
.react-flow__edge.selectable {
    cursor: pointer;
  }
.react-flow__edge.animated path {
    stroke-dasharray: 5;
    animation: dashdraw 0.5s linear infinite;
  }
.react-flow__edge.animated path.react-flow__edge-interaction {
    stroke-dasharray: none;
    animation: none;
  }
.react-flow__edge.inactive {
    pointer-events: none;
  }
.react-flow__edge.selected,
  .react-flow__edge:focus,
  .react-flow__edge:focus-visible {
    outline: none;
  }
.react-flow__edge.selected .react-flow__edge-path,
  .react-flow__edge.selectable:focus .react-flow__edge-path,
  .react-flow__edge.selectable:focus-visible .react-flow__edge-path {
    stroke: var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default));
  }
.react-flow__edge-textwrapper {
    pointer-events: all;
  }
.react-flow__edge .react-flow__edge-text {
    pointer-events: none;
    -webkit-user-select: none;
       -moz-user-select: none;
            user-select: none;
  }
/* Arrowhead marker styles - use CSS custom properties as default */
.react-flow__arrowhead polyline {
  stroke: var(--xy-edge-stroke, var(--xy-edge-stroke-default));
}
.react-flow__arrowhead polyline.arrowclosed {
  fill: var(--xy-edge-stroke, var(--xy-edge-stroke-default));
}
.react-flow__connection {
  pointer-events: none;
}
.react-flow__connection .animated {
    stroke-dasharray: 5;
    animation: dashdraw 0.5s linear infinite;
  }
svg.react-flow__connectionline {
  z-index: 1001;
  overflow: visible;
  position: absolute;
}
.react-flow__nodes {
  pointer-events: none;
  transform-origin: 0 0;
}
.react-flow__node {
  position: absolute;
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
  pointer-events: all;
  transform-origin: 0 0;
  box-sizing: border-box;
  cursor: default;
}
.react-flow__node.selectable {
    cursor: pointer;
  }
.react-flow__node.draggable {
    cursor: grab;
    pointer-events: all;
  }
.react-flow__node.draggable.dragging {
      cursor: grabbing;
    }
.react-flow__nodesselection {
  z-index: 3;
  transform-origin: left top;
  pointer-events: none;
}
.react-flow__nodesselection-rect {
    position: absolute;
    pointer-events: all;
    cursor: grab;
  }
.react-flow__handle {
  position: absolute;
  pointer-events: none;
  min-width: 5px;
  min-height: 5px;
  width: 6px;
  height: 6px;
  background-color: var(--xy-handle-background-color, var(--xy-handle-background-color-default));
  border: 1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));
  border-radius: 100%;
}
.react-flow__handle.connectingfrom {
    pointer-events: all;
  }
.react-flow__handle.connectionindicator {
    pointer-events: all;
    cursor: crosshair;
  }
.react-flow__handle-bottom {
    top: auto;
    left: 50%;
    bottom: 0;
    transform: translate(-50%, 50%);
  }
.react-flow__handle-top {
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
  }
.react-flow__handle-left {
    top: 50%;
    left: 0;
    transform: translate(-50%, -50%);
  }
.react-flow__handle-right {
    top: 50%;
    right: 0;
    transform: translate(50%, -50%);
  }
.react-flow__edgeupdater {
  cursor: move;
  pointer-events: all;
}
.react-flow__pane.selection .react-flow__panel {
  pointer-events: none;
}
.react-flow__panel {
  position: absolute;
  z-index: 5;
  margin: 15px;
}
.react-flow__panel.top {
    top: 0;
  }
.react-flow__panel.bottom {
    bottom: 0;
  }
.react-flow__panel.top.center, .react-flow__panel.bottom.center {
      left: 50%;
      transform: translateX(-15px) translateX(-50%);
    }
.react-flow__panel.left {
    left: 0;
  }
.react-flow__panel.right {
    right: 0;
  }
.react-flow__panel.left.center, .react-flow__panel.right.center {
      top: 50%;
      transform: translateY(-15px) translateY(-50%);
    }
.react-flow__attribution {
  font-size: 10px;
  background: var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));
  padding: 2px 3px;
  margin: 0;
}
.react-flow__attribution a {
    text-decoration: none;
    color: #999;
  }
@keyframes dashdraw {
  from {
    stroke-dashoffset: 10;
  }
}
.react-flow__edgelabel-renderer {
  position: absolute;
  width: 100%;
  height: 100%;
  pointer-events: none;
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
  left: 0;
  top: 0;
}
.react-flow__viewport-portal {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
}
.react-flow__minimap {
  background: var(
    --xy-minimap-background-color-props,
    var(--xy-minimap-background-color, var(--xy-minimap-background-color-default))
  );
}
.react-flow__minimap-svg {
    display: block;
  }
.react-flow__minimap-mask {
    fill: var(
      --xy-minimap-mask-background-color-props,
      var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default))
    );
    stroke: var(
      --xy-minimap-mask-stroke-color-props,
      var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default))
    );
    stroke-width: var(
      --xy-minimap-mask-stroke-width-props,
      var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default))
    );
  }
.react-flow__minimap-node {
    fill: var(
      --xy-minimap-node-background-color-props,
      var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default))
    );
    stroke: var(
      --xy-minimap-node-stroke-color-props,
      var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default))
    );
    stroke-width: var(
      --xy-minimap-node-stroke-width-props,
      var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default))
    );
  }
.react-flow__background-pattern.dots {
    fill: var(
      --xy-background-pattern-color-props,
      var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default))
    );
  }
.react-flow__background-pattern.lines {
    stroke: var(
      --xy-background-pattern-color-props,
      var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default))
    );
  }
.react-flow__background-pattern.cross {
    stroke: var(
      --xy-background-pattern-color-props,
      var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default))
    );
  }
.react-flow__controls {
  display: flex;
  flex-direction: column;
  box-shadow: var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default));
}
.react-flow__controls.horizontal {
    flex-direction: row;
  }
.react-flow__controls-button {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 26px;
    width: 26px;
    padding: 4px;
    border: none;
    background: var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));
    border-bottom: 1px solid
      var(
        --xy-controls-button-border-color-props,
        var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default))
      );
    color: var(
      --xy-controls-button-color-props,
      var(--xy-controls-button-color, var(--xy-controls-button-color-default))
    );
    cursor: pointer;
    -webkit-user-select: none;
       -moz-user-select: none;
            user-select: none;
  }
.react-flow__controls-button svg {
      width: 100%;
      max-width: 12px;
      max-height: 12px;
      fill: currentColor;
    }
.react-flow__edge.updating .react-flow__edge-path {
      stroke: #777;
    }
.react-flow__edge-text {
    font-size: 10px;
  }
.react-flow__node.selectable:focus,
  .react-flow__node.selectable:focus-visible {
    outline: none;
  }
.react-flow__node-input,
.react-flow__node-default,
.react-flow__node-output,
.react-flow__node-group {
  padding: 10px;
  border-radius: var(--xy-node-border-radius, var(--xy-node-border-radius-default));
  width: 150px;
  font-size: 12px;
  color: var(--xy-node-color, var(--xy-node-color-default));
  text-align: center;
  border: var(--xy-node-border, var(--xy-node-border-default));
  background-color: var(--xy-node-background-color, var(--xy-node-background-color-default));
}
.react-flow__node-input.selectable:hover, .react-flow__node-default.selectable:hover, .react-flow__node-output.selectable:hover, .react-flow__node-group.selectable:hover {
      box-shadow: var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default));
    }
.react-flow__node-input.selectable.selected,
    .react-flow__node-input.selectable:focus,
    .react-flow__node-input.selectable:focus-visible,
    .react-flow__node-default.selectable.selected,
    .react-flow__node-default.selectable:focus,
    .react-flow__node-default.selectable:focus-visible,
    .react-flow__node-output.selectable.selected,
    .react-flow__node-output.selectable:focus,
    .react-flow__node-output.selectable:focus-visible,
    .react-flow__node-group.selectable.selected,
    .react-flow__node-group.selectable:focus,
    .react-flow__node-group.selectable:focus-visible {
      box-shadow: var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default));
    }
.react-flow__node-group {
  background-color: var(--xy-node-group-background-color, var(--xy-node-group-background-color-default));
}
.react-flow__nodesselection-rect,
.react-flow__selection {
  background: var(--xy-selection-background-color, var(--xy-selection-background-color-default));
  border: var(--xy-selection-border, var(--xy-selection-border-default));
}
.react-flow__nodesselection-rect:focus,
  .react-flow__nodesselection-rect:focus-visible,
  .react-flow__selection:focus,
  .react-flow__selection:focus-visible {
    outline: none;
  }
.react-flow__controls-button:hover {
      background: var(
        --xy-controls-button-background-color-hover-props,
        var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default))
      );
      color: var(
        --xy-controls-button-color-hover-props,
        var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default))
      );
    }
.react-flow__controls-button:disabled {
      pointer-events: none;
    }
.react-flow__controls-button:disabled svg {
        fill-opacity: 0.4;
      }
.react-flow__controls-button:last-child {
    border-bottom: none;
  }
.react-flow__controls.horizontal .react-flow__controls-button {
    border-bottom: none;
    border-right: 1px solid
      var(
        --xy-controls-button-border-color-props,
        var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default))
      );
  }
.react-flow__controls.horizontal .react-flow__controls-button:last-child {
    border-right: none;
  }
.react-flow__resize-control {
  position: absolute;
}
.react-flow__resize-control.left,
.react-flow__resize-control.right {
  cursor: ew-resize;
}
.react-flow__resize-control.top,
.react-flow__resize-control.bottom {
  cursor: ns-resize;
}
.react-flow__resize-control.top.left,
.react-flow__resize-control.bottom.right {
  cursor: nwse-resize;
}
.react-flow__resize-control.bottom.left,
.react-flow__resize-control.top.right {
  cursor: nesw-resize;
}
/* handle styles */
.react-flow__resize-control.handle {
  width: 5px;
  height: 5px;
  border: 1px solid #fff;
  border-radius: 1px;
  background-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default));
  translate: -50% -50%;
}
.react-flow__resize-control.handle.left {
  left: 0;
  top: 50%;
}
.react-flow__resize-control.handle.right {
  left: 100%;
  top: 50%;
}
.react-flow__resize-control.handle.top {
  left: 50%;
  top: 0;
}
.react-flow__resize-control.handle.bottom {
  left: 50%;
  top: 100%;
}
.react-flow__resize-control.handle.top.left {
  left: 0;
}
.react-flow__resize-control.handle.bottom.left {
  left: 0;
}
.react-flow__resize-control.handle.top.right {
  left: 100%;
}
.react-flow__resize-control.handle.bottom.right {
  left: 100%;
}
/* line styles */
.react-flow__resize-control.line {
  border-color: var(--xy-resize-background-color, var(--xy-resize-background-color-default));
  border-width: 0;
  border-style: solid;
}
.react-flow__resize-control.line.left,
.react-flow__resize-control.line.right {
  width: 1px;
  transform: translate(-50%, 0);
  top: 0;
  height: 100%;
}
.react-flow__resize-control.line.left {
  left: 0;
  border-left-width: 1px;
}
.react-flow__resize-control.line.right {
  left: 100%;
  border-right-width: 1px;
}
.react-flow__resize-control.line.top,
.react-flow__resize-control.line.bottom {
  height: 1px;
  transform: translate(0, -50%);
  left: 0;
  width: 100%;
}
.react-flow__resize-control.line.top {
  top: 0;
  border-top-width: 1px;
}
.react-flow__resize-control.line.bottom {
  border-bottom-width: 1px;
  top: 100%;
}
.react-flow__edge-textbg {
  fill: var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default));
}
.react-flow__edge-text {
  fill: var(--xy-edge-label-color, var(--xy-edge-label-color-default));
}
.board {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  min-width: 0;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 10px 16px;
  border-bottom: 1px solid #d0d3da;
  background: #fafafa;
  flex-wrap: wrap;
}

.toolbar__checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.toolbar__count {
  margin-left: auto;
  font-size: 12px;
  color: #777;
  font-family: ui-monospace, monospace;
}

/* --- 검색 하이라이트 + 자동 이동 --- */
.toolbar__search {
  padding: 4px 8px;
  border: 1px solid #d0d3da;
  border-radius: 6px;
  font-size: 12px;
  font-family: ui-monospace, monospace;
  min-width: 160px;
}

.toolbar__search-count {
  font-size: 12px;
  color: #6b7280;
  font-family: ui-monospace, monospace;
  white-space: nowrap;
}

/* --- 다크모드 토글 --- */
.toolbar__theme-toggle {
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid #6366f1;
  background: #eef2ff;
  color: #312e81;
  font-family: ui-monospace, monospace;
  font-size: 12px;
  cursor: pointer;
}

/* --- 캔버스 스티키노트(ADR-0029) 추가 버튼 --- */
.toolbar__sticky-add {
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid #eab308;
  background: #fef9c3;
  color: #713f12;
  font-family: ui-monospace, monospace;
  font-size: 12px;
  cursor: pointer;
}

.canvas {
  flex: 1;
  min-height: 0;
}

/* --- 영역(region) 프레임: 뭉치지 않고 경계만 그어준다 (ui-philosophy.md) --- */
.group-node {
  width: 100%;
  height: 100%;
  border: 1.5px dashed #9aa3b5;
  border-radius: 12px;
  background: rgba(148, 163, 184, 0.08);
  transition: width 200ms ease, height 200ms ease;
}

.group-node--pending {
  border-style: dotted;
  border-color: #c4c9d4;
  background: rgba(148, 163, 184, 0.03);
}

/* 뷰포트 밖(또는 지도 모드)이라 자식을 펼치지 않은 그룹 — 프레임/라벨/개수는 정확하지만
   내부 노드는 아직(또는 이 줌 레벨에서는 앞으로도) 그리지 않는다 (ADR-0016 ①). */
.group-node--collapsed {
  background: repeating-linear-gradient(
    135deg,
    rgba(148, 163, 184, 0.06),
    rgba(148, 163, 184, 0.06) 8px,
    rgba(148, 163, 184, 0.12) 8px,
    rgba(148, 163, 184, 0.12) 16px
  );
}

.group-node__handle {
  opacity: 0;
  pointer-events: none;
}

/* 도메인별 커스텀 팔레트 (groupColor.ts) — 인덱스 순서는 그 파일의 PALETTE 배열과 반드시
   일치해야 한다(하나를 바꾸면 다른 쪽도 같이 바꿀 것). 그룹 프레임은 rough-border 대상이
   아니라(컴포넌트 노드 섹션 참고) 그냥 border-color로 표현한다. */
.group-node--palette-0 { border-color: #6366f1; }
.group-node--palette-1 { border-color: #2563eb; }
.group-node--palette-2 { border-color: #0e7490; }
.group-node--palette-3 { border-color: #0f766e; }
.group-node--palette-4 { border-color: #7c3aed; }
.group-node--palette-5 { border-color: #a21caf; }
.group-node--palette-6 { border-color: #db2777; }
.group-node--palette-7 { border-color: #e11d48; }

.group-node__header {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px;
  font-family: ui-monospace, monospace;
  transition: opacity 100ms ease;
}

/* 지도 모드에서는 counter-scale된 라벨의 실제 화면 사각형이 충돌하면, GroupLabelDeclutter가
   우선순위가 낮은 헤더만 숨긴다. 프레임/색/집계 엣지는 남겨 '영역 지도' 자체는 보존한다. */
.group-node__header--decluttered {
  opacity: 0;
  pointer-events: none;
}

/* 그룹 접기/펼치기 셰브런(ADR-0029) — 라벨/개수와 같은 counter-scale 위치에 있어 어느 줌
   레벨에서도 항상 같은 크기로 클릭 가능하다. */
.group-node__toggle {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font-size: 13px;
  color: #64748b;
  line-height: 1;
}

.group-node__label {
  font-weight: 600;
  font-size: 13px;
  color: #334155;
}

.group-node--pending .group-node__label {
  color: #94a3b8;
  font-style: italic;
}

.group-node__count {
  font-size: 11px;
  color: #94a3b8;
}

/* --- 폴더 프레임(폴더 단위 2단 중첩, ADR-0053) --- */
/* 파일 프레임(group-node)보다 뒤(zIndex:-2)에서 이들을 감싸는 바깥 프레임. 파일 프레임의 점선과
   구분되게 은은한 실선 + 살짝 진한 배경으로 "이 파일들이 한 폴더"임을 표시한다. */
.folder-node {
  width: 100%;
  height: 100%;
  border: 1.5px solid rgba(100, 116, 139, 0.45);
  border-radius: 16px;
  background: rgba(100, 116, 139, 0.05);
}

.folder-node__header {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 8px 14px;
  font-family: ui-monospace, monospace;
}

.folder-node__icon {
  font-size: 12px;
  line-height: 1;
}

.folder-node__label {
  font-weight: 700;
  font-size: 13px;
  color: #475569;
  letter-spacing: 0.01em;
}

.folder-node__count {
  font-size: 11px;
  color: #94a3b8;
}

/* --- 컴포넌트 노드 --- */
/* 테두리는 더 이상 CSS border가 아니라 roughStyle.ts가 만든 손그림 스케치 이미지다(kind별로
   ComponentNode.tsx가 background-image 인라인 스타일로 얹는다) — 노드 크기가 고정(NODE_WIDTH/
   NODE_HEIGHT)이라 정적 이미지 하나를 모든 노드가 공유해도 어긋나지 않는다. */
.component-node {
  position: relative; /* --highlighted::after 손그림 강조 링의 기준 (ADR-0035 후속) */
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 0;
  background-color: #eef2ff;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  font-size: 12px;
  font-family: ui-monospace, monospace;
  color: #312e81;
  padding: 4px 8px;
  text-align: center;
  transition: opacity 120ms ease;
}

.component-node--host {
  background-color: #f8fafc;
  color: #64748b;
}

.component-node--anonymous {
  opacity: 0.6;
}

.component-node--cross-group {
  box-shadow: 0 0 0 2px #f59e0b55;
}

.component-node--pending {
  opacity: 0.5;
}

/* 검색 매치(ADR-0027 검색+자동 이동). rough-border가 이미 배경 위에 스케치를 그리므로
   border-color를 쓸 수 없다(GroupNode와 달리 컴포넌트 노드는 border: none) — outline은
   border-box 바깥에 그려져 배경/스케치/box-shadow(--cross-group, --highlighted) 어느 것과도
   겹치지 않는다. */
.component-node--matched {
  outline: 2px solid #16a34a;
  outline-offset: 2px;
}

/* 검색어가 있는 동안 매치 안 된 노드를 흐리게 — "매치=일반, 나머지=흐림"이 아니라 그
   반대라 :not()으로 표현한다. */
.canvas.search-active .component-node:not(.component-node--matched) {
  opacity: 0.25;
}

/* 도메인별 커스텀 팔레트 (groupColor.ts) — GroupNode의 palette 규칙과 인덱스가 반드시
   일치해야 한다. rough-border는 fill:none인 스케치 선일 뿐이라 background-color 위에 얹혀도
   가려지지 않으므로, 컴포넌트 노드의 도메인 색은 옅은 배경 틴트로 표현한다(테두리 색 대신). */
.component-node--palette-0 { background-color: #6366f11a; }
.component-node--palette-1 { background-color: #2563eb1a; }
.component-node--palette-2 { background-color: #0e74901a; }
.component-node--palette-3 { background-color: #0f766e1a; }
.component-node--palette-4 { background-color: #7c3aed1a; }
.component-node--palette-5 { background-color: #a21caf1a; }
.component-node--palette-6 { background-color: #db27771a; }
.component-node--palette-7 { background-color: #e11d481a; }

/* ADR-0093: 밀집 모드는 파일 프레임을 바꾸지 않고, 폭을 만드는 부모의 직접 자식 가지를
   같은 파일 안의 한 장의 카드로 접는다. 카드 전체가 "이 부분만 strict로 펼치기" 제어다. */
.component-node--compact-summary {
  background-color: #fff7ed;
}
.component-node__compact-summary-button {
  display: grid;
  grid-template-columns: 1fr auto;
  width: 100%;
  height: 100%;
  align-items: center;
  gap: 2px 8px;
  border: 0;
  background: transparent;
  color: #9a3412;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.component-node__compact-summary-title {
  font-weight: 800;
}
.component-node__compact-summary-copy {
  grid-column: 1;
  font-size: 9px;
  color: #b45309;
  white-space: nowrap;
}
.component-node__compact-summary-open {
  grid-column: 2;
  grid-row: 1 / span 2;
  font-size: 18px;
}
.component-node__compact-control {
  border: 1px solid #fdba74;
  border-radius: 999px;
  background: #fffbeb;
  color: #9a3412;
  padding: 3px 7px;
  font: 700 10px ui-monospace, monospace;
  cursor: pointer;
  white-space: nowrap;
}
.component-node__compact-control:hover { background: #ffedd5; }
body.rrb-dark-mode .component-node--compact-summary { background-color: #431407; }
body.rrb-dark-mode .component-node__compact-summary-button { color: #fed7aa; }
body.rrb-dark-mode .component-node__compact-summary-copy { color: #fdba74; }
body.rrb-dark-mode .component-node__compact-control {
  border-color: #9a3412;
  background: #431407;
  color: #fed7aa;
}

.component-node__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ADR-0093: raw host는 캔버스 노드로 추가하지 않는다. 선택한 component 하나에만 NodeToolbar
   포탈로 tag ×N 요약을 띄워, 상세를 볼 수 있으면서도 waterfall 배치/다른 그룹을 침범하지 않는다. */
.component-node__host-detail {
  display: flex;
  align-items: center;
  gap: 5px;
  max-width: 360px;
  padding: 5px 8px;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  background: #fffbeb;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.16);
  color: #475569;
  font-family: ui-monospace, monospace;
  font-size: 10px;
  white-space: nowrap;
}
.component-node__host-detail-title {
  color: #92400e;
  font-weight: 700;
}
.component-node__host-detail-item {
  padding-left: 5px;
  border-left: 1px solid #d6d3d1;
}
body.rrb-dark-mode .component-node__host-detail {
  border-color: #475569;
  background: #292524;
  color: #cbd5e1;
}
body.rrb-dark-mode .component-node__host-detail-title {
  color: #fbbf24;
}

/* 리스트 접기(ADR-0046) "×N" 배지 — 대표 노드가 실은 같은 형제 N개를 접은 것임을 알린다.
   노드 오른쪽 위 모서리에 작은 알약으로. */
.component-node__count-badge {
  position: absolute;
  top: -7px;
  right: -7px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: #6366f1;
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
}
body.rrb-dark-mode .component-node__count-badge {
  background: #818cf8;
  color: #0b1120;
}
/* 폴더 프레임 다크(ADR-0053) — 어두운 배경에서 테두리/라벨이 묻히지 않게 밝힌다. */
body.rrb-dark-mode .folder-node {
  border-color: rgba(148, 163, 184, 0.5);
  background: rgba(148, 163, 184, 0.06);
}
body.rrb-dark-mode .folder-node__label {
  color: #cbd5e1;
}

/* 크로스-그룹 간선: 실선(ADR-0059, 사용자 피드백). 원래 점선("선 스타일 채널 = 그룹 내 실선 /
   경계 점선", ADR-0047)이었으나 두 이유로 실선 통일: (1) 점선이 stroke를 조각내 그라데이션 색
   연속성을 끊어 "끝색=어디로 가나"가 안 읽혔다. (2) 경계 넘김은 이미 프레임 밖으로 나가는 것으로
   보이고 색 그라데이션이 정체성을 지녀 점선 채널은 중복. 그라데이션이 읽히게 살짝 두껍게(1.75). */
.react-flow__edge.edge-cross-group .react-flow__edge-path {
  stroke-width: 1.75;
}

/* 공유 UI 사용 칩(pillar ②, ADR-0061): 사용처 노드에 붙는 "↘X 공유" 인라인 칩. 상시 긴 선을
   폐지하고 로컬 표식으로 대체 — 화면 가로지르는 선 없이 "여기 공유 X 씀"이 사용처에서 읽힌다
   (전체 연결은 호버 시 점등, 후속). 노드 아래로 살짝 넘치게 절대 배치해 이름/배지를 안 가린다. */
.component-node__shared-uses {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: -10px;
  display: flex;
  gap: 4px;
  max-width: 130%;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
}
/* 솔리드 pill 버튼 — 배경색은 인라인(그 공유 컨테이너의 팔레트 색)으로 통일, 여기선 형태만. 흰
   테두리 + 그림자로 어떤 노드 위에서도 또렷하고 멀리서도 색 덩어리로 읽힌다. 클릭하면 인라인 peek
   토글이라 pointer-events를 되살린다(부모 __shared-uses는 none). */
.component-node__shared-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: inherit;
  font-size: 9px;
  font-weight: 700;
  line-height: 1.5;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1.5px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25);
  white-space: nowrap;
  letter-spacing: 0.02em;
  cursor: pointer;
  pointer-events: auto;
}
.component-node__shared-chip--open {
  box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.15), 0 1px 3px rgba(15, 23, 42, 0.25);
}
body.rrb-dark-mode .component-node__shared-chip {
  border-color: rgba(15, 23, 42, 0.85);
}
.component-node__shared-chip-glyph {
  font-size: 10px;
  line-height: 1;
  opacity: 0.85;
}
/* 인라인 peek 카드 — 칩 아래로 펼쳐지는 미리보기(접힌 실제 인스턴스 내용). 점선 테두리로 "실제
   트리 노드가 아니라 잠깐 펼친 미리보기"임을 표시(설계 문서). 노드 위로 떠서 z-index 높게. */
.component-node__shared-peek {
  position: absolute;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 96px;
  max-width: 180px;
  padding: 5px 7px;
  border: 1.5px dashed;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.22);
  cursor: default;
  white-space: nowrap;
}
body.rrb-dark-mode .component-node__shared-peek {
  background: #1e293b;
}
.component-node__shared-peek-title {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-bottom: 1px;
}
.component-node__shared-peek-item {
  font-size: 9px;
  font-weight: 500;
  color: #475569;
  padding: 1px 4px;
  border-radius: 4px;
  background: rgba(100, 116, 139, 0.1);
  text-align: left;
}
body.rrb-dark-mode .component-node__shared-peek-item {
  color: #cbd5e1;
}

/* 호버 on-demand 공유 연결선(pillar ②) — 사용처↔공유 그룹 직선. 색은 인라인(공유 그룹 팔레트)
   으로 칩·레인과 통일. 평소엔 없고 호버 때만 생겨, "전체 연결 보기"를 필요할 때만 준다. */
.react-flow__edge.edge-shared-ondemand .react-flow__edge-path {
  stroke-dasharray: 6 3;
  filter: drop-shadow(0 0 2px rgba(15, 23, 42, 0.2));
  /* 애니메이션 방향 반전(사용자 요청): 간선은 사용처(source)→공유 UI(target)로 잇지만, 흐름은
     반대로 공유 UI에서 사용처로 흐르게 해 "UI를 끌어다 쓰는" 느낌을 준다. React Flow animated의
     dashdraw(source→target)를 reverse로 뒤집는다. */
  animation-direction: reverse;
}

/* 공유 레인 그룹 프레임 — 아래 별도 밴드에 있는 다중부모 컨테이너. 살짝 다른 배경으로 "이 띠는
   트리가 아니라 공유 자원"임을 알린다(스윔레인 언어, 설계 문서). */
.group-node.group-node--shared {
  background: repeating-linear-gradient(
    -45deg,
    rgba(100, 116, 139, 0.06),
    rgba(100, 116, 139, 0.06) 6px,
    rgba(100, 116, 139, 0.11) 6px,
    rgba(100, 116, 139, 0.11) 12px
  );
}
.group-node__usage {
  margin-left: 6px;
  font-size: 11px;
  font-weight: 600;
  color: #64748b;
  white-space: nowrap;
}
body.rrb-dark-mode .group-node__usage {
  color: #94a3b8;
}

/* --- 간선 3채널: opacity=깊이 감쇠 / hue=부모 도메인 / 선 스타일=그룹 내·경계 --- */
/* (ADR-0029 결정 #4 감쇠 + ADR-0044 후속 부모색). 그룹 내 간선은 hairline으로 얇게 하되(잉크
   절약), 색은 회색이 아니라 부모 도메인 색으로 통일한다 — 같은-그룹은 부모가 그 그룹 안이라
   그룹당 단색이 돼 haze가 아니라 "이 도메인의 배선"으로 읽히고, opacity 감쇠는 그대로라
   클러터도 안 는다(색과 감쇠는 독립 채널). 팔레트 미배정(pending)일 때의 폴백 회색. */
.react-flow__edge.edge-same-group .react-flow__edge-path {
  stroke: #cbd5e1;
  stroke-width: 1;
}
body.rrb-dark-mode .react-flow__edge.edge-same-group .react-flow__edge-path {
  stroke: #475569;
}
/* 부모(출발) 도메인 색 — toFlow가 모든 노드 간선에 edge-parent-palette-N을 상시로 붙인다.
   node/group 팔레트와 같은 hex(groupColor PALETTE). !important 없이 둬서 hover 혈통(.lineage-active)
   과 props 흐름 장식(edge-tracked/hot)의 !important 강조가 이 상시 색을 이긴다. */
.react-flow__edge.edge-parent-palette-0 .react-flow__edge-path { stroke: #6366f1; }
.react-flow__edge.edge-parent-palette-1 .react-flow__edge-path { stroke: #2563eb; }
.react-flow__edge.edge-parent-palette-2 .react-flow__edge-path { stroke: #0e7490; }
.react-flow__edge.edge-parent-palette-3 .react-flow__edge-path { stroke: #0f766e; }
.react-flow__edge.edge-parent-palette-4 .react-flow__edge-path { stroke: #7c3aed; }
.react-flow__edge.edge-parent-palette-5 .react-flow__edge-path { stroke: #a21caf; }
.react-flow__edge.edge-parent-palette-6 .react-flow__edge-path { stroke: #db2777; }
.react-flow__edge.edge-parent-palette-7 .react-flow__edge-path { stroke: #e11d48; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-0 .react-flow__edge-path { stroke: #818cf8; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-1 .react-flow__edge-path { stroke: #60a5fa; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-2 .react-flow__edge-path { stroke: #22d3ee; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-3 .react-flow__edge-path { stroke: #2dd4bf; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-4 .react-flow__edge-path { stroke: #a78bfa; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-5 .react-flow__edge-path { stroke: #e879f9; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-6 .react-flow__edge-path { stroke: #f472b6; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-7 .react-flow__edge-path { stroke: #fb7185; }
/* 그룹 내 깊이가 깊을수록 더 옅게(toFlow가 붙이는 edge-depth-N, N=1..3, 3은 "3 이상"). 깊은
   간선일수록 트리 위치가 이미 관계를 말해주므로 잉크를 더 줄인다. */
.react-flow__edge.edge-depth-1 { opacity: 0.78; }
.react-flow__edge.edge-depth-2 { opacity: 0.62; }
.react-flow__edge.edge-depth-3 { opacity: 0.48; }
/* props 흐름 장식(ADR-0032)은 감쇠 대상이 아니다 — 추적/활동 간선은 클러터가 아니라 신호라
   깊이 opacity를 무시하고 또렷하게 유지한다. */
.react-flow__edge.edge-tracked,
.react-flow__edge.edge-hot {
  opacity: 1;
}

/* --- hover 혈통 점등(ADR-0041 후속, 연구문서 7절 c) --- */
/* 컴포넌트 노드에 hover하면 canvas에 lineage-active가 붙는다(Canvas.tsx). 그 노드의 조상 체인+
   자손 서브트리 간선(edge-lineage)만 남기고 나머지를 거의 지운다 — 상시 감쇠로 못 없앤 마지막
   클러터를 인터랙션으로 해소("지금 관심 있는 혈통만 잉크"). 검색 dimming(.search-active)과 같은
   메커니즘. 추적/활동 간선은 신호라 dim 예외. */
/* 혈통 아닌 간선은 거의 지운다(hover 이상 강하게 대비시키려고 아주 낮게). 단 프레임-폴백 간선
   (edge-cross-group-frame, source가 화면 밖 그룹 프레임이라 혈통 탐색에서 빠짐)은 dim 예외 —
   안 그러면 호버할 때 "저 도메인에서 들어오는 연결"이 통째로 사라진다(피드백 2). */
.canvas.lineage-active
  .react-flow__edge:not(.edge-lineage):not(.edge-tracked):not(.edge-hot):not(.edge-cross-group-frame):not(.edge-shared-ondemand) {
  opacity: 0.04;
}
/* 노드 쪽 짝(ComponentNode가 LineageNodesContext로 판정): 혈통 밖 노드는 흐리게 죽여, 혈통
   노드+간선이 하나의 경로로 도드라지게 한다. 혈통 안 노드는 살짝 띄운다. 검색 dimming과 같은
   느낌(값만 조금 더 강함)이라 검색과 겹쳐도 자연스럽다. */
.component-node--lineage-off {
  opacity: 0.18;
  transition: opacity 120ms ease;
}
/* "지도에서도 상세"(ADR-0049)일 땐 hover 혈통 노드 dimming을 끈다 — 넓게 다 보려고 켠 모드인데
   빽빽한 노드 위 아무 데나 마우스가 얹히면 그 혈통 외 전부가 흐려져 "다 페이드"로 보이기 때문
   (wideDetail=다 보기 vs hover-dimming=하나만 강조가 상충). 간선 혈통 강조(edge-lineage)는 유지해
   경로는 여전히 도드라진다. */
.canvas.wide-detail .component-node--lineage-off {
  opacity: 1;
}
.component-node--lineage-on {
  filter: drop-shadow(0 0 4px rgba(99, 102, 241, 0.35));
}
/* 점등된 혈통 간선: 깊이 감쇠/LOD를 무시하고 굵고 또렷하게(width 3 + 은은한 글로우) 되살린다.
   색은 아래 edge-parent-palette-N이 "부모(source) 노드의 도메인 색"으로 덮어써 "이 선이 어느
   도메인에서 왔나"를 드러낸다(선 정체성). 그룹 간 간선의 주황 !important보다 셀렉터 특이도가
   높아 팔레트 색이 이긴다. */
.canvas.lineage-active .react-flow__edge.edge-lineage {
  opacity: 1;
}
.canvas.lineage-active .react-flow__edge.edge-lineage .react-flow__edge-path {
  stroke: #6366f1 !important; /* 팔레트 미배정(pending) 폴백 */
  stroke-width: 3 !important;
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.25));
}
/* 부모 도메인 색(groupColor PALETTE, 인덱스별 light). node/group 팔레트 클래스와 같은 hex. */
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-0 .react-flow__edge-path { stroke: #6366f1 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-1 .react-flow__edge-path { stroke: #2563eb !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-2 .react-flow__edge-path { stroke: #0e7490 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-3 .react-flow__edge-path { stroke: #0f766e !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-4 .react-flow__edge-path { stroke: #7c3aed !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-5 .react-flow__edge-path { stroke: #a21caf !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-6 .react-flow__edge-path { stroke: #db2777 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-7 .react-flow__edge-path { stroke: #e11d48 !important; }
/* 다크 변형(PALETTE[i].dark) — 어두운 배경에서 채도/명도 확보. */
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage .react-flow__edge-path { stroke: #818cf8 !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-0 .react-flow__edge-path { stroke: #818cf8 !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-1 .react-flow__edge-path { stroke: #60a5fa !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-2 .react-flow__edge-path { stroke: #22d3ee !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-3 .react-flow__edge-path { stroke: #2dd4bf !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-4 .react-flow__edge-path { stroke: #a78bfa !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-5 .react-flow__edge-path { stroke: #e879f9 !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-6 .react-flow__edge-path { stroke: #f472b6 !important; }
body.rrb-dark-mode .canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-7 .react-flow__edge-path { stroke: #fb7185 !important; }

/* --- 캔버스 스티키노트(ADR-0029) --- */
.sticky-note {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  background: #fef9c3;
  border: 1px solid #eab308;
  border-radius: 4px;
  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.15);
}

.sticky-note__delete {
  align-self: flex-end;
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: #78716c;
  padding: 0 2px;
}

.sticky-note__text {
  flex: 1;
  min-height: 0;
  resize: none;
  border: none;
  background: transparent;
  font-family: ui-monospace, monospace;
  font-size: 12px;
  color: #422006;
}

.sticky-note__text:focus {
  outline: none;
}

/* --- 우클릭 컨텍스트 메뉴(ADR-0029) --- */
.context-menu {
  position: fixed;
  z-index: 2147483001;
  background: #fff;
  border: 1px solid #d0d3da;
  border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  padding: 4px;
  min-width: 170px;
  font-family: ui-monospace, monospace;
}

.context-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 10px;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: 4px;
  font-size: 12px;
  color: #1f2933;
}

.context-menu__item:hover {
  background: #eef2ff;
}

/* 커밋마다 노드/그룹이 옮겨질 수 있으므로 위치 변화를 부드러운 애니메이션으로 보여준다
   (layout.ts의 "그룹 단위 메모이제이션"과 짝을 이루는 시각적 장치 — ADR-0008 참고). */
.react-flow__node {
  transition: transform 200ms ease, width 200ms ease, height 200ms ease;
}

/* --- semantic zoom: 줌 아웃 시 지도 모드(영역만), 줌 인 시 상세 모드 --- */
.zoom-far .component-node,
.zoom-far .react-flow__edge {
  opacity: 0;
  pointer-events: none;
}

/* "지도에서도 상세"(ADR-0049): 지도 모드여도 컴포넌트 노드/실제 관계선을 함께 되살린다.
   확대율은 같은 관계를 숨기거나 보이게만 할 뿐, 다른 파일 집계선으로 교체하지 않는다. */
.zoom-far.wide-detail .component-node,
.zoom-far.wide-detail .react-flow__edge {
  opacity: 1;
  pointer-events: auto;
}

/* GroupNode.tsx가 캔버스 줌의 역수를 곱해 라벨을 화면 기준 고정 크기로 유지한다
   (ADR-0016 ②) — 여기서 폰트 크기를 다시 키우면 이중으로 확대돼 오히려 어긋난다. */

.zoom-badge {
  background: #111827dd;
  color: #f9fafb;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 12px;
  font-family: ui-monospace, monospace;
}

/* --- 보드↔DOM 양방향 인터랙션 (ADR-0024/0025) --- */

/* 정방향(보드 노드 클릭)·역방향 착지 지점 표시. 예전엔 네온 box-shadow 글로우였는데 손그림
   정체성과 이질적이라(사용자 피드백 "글로우 너무 구리다") "마커로 한 번 동그라미 친" 손그림
   링으로 바꿨다(roughStyle.HIGHLIGHT_RING, ADR-0035 후속). ::after로 노드보다 살짝 크게 얹고
   opacity만 은은하게 펄스한다 — transform은 React Flow가 노드 위치에 쓰므로 절대 건드리지 않고
   (건드리면 노드가 제자리를 벗어난다), blur 글로우도 안 쓴다. 링 이미지는 board-panel의 CSS
   변수(BoardOverlay.tsx)로 내려받아 shared Canvas.tsx를 건드리지 않는다. */
.component-node--highlighted::after {
  content: '';
  position: absolute;
  inset: -4px;
  background: var(--rrb-highlight-ring-light) center / 100% 100% no-repeat;
  pointer-events: none;
  animation: rrb-attention 1.3s ease-in-out infinite;
}

body.rrb-dark-mode .component-node--highlighted::after {
  background-image: var(--rrb-highlight-ring-dark);
}

@keyframes rrb-attention {
  0%,
  100% {
    opacity: 0.4;
  }
  50% {
    opacity: 1;
  }
}

/* ADR-0020의 플로팅 버튼 + 전체화면 오버레이 셸(BoardOverlay.tsx). 2147483000 근방의 매우
   높은 z-index는 experiments/real-app-validation의 mount.tsx 검증에서 그대로 가져왔다 —
   이 도구가 임의의 호스트 앱(어떤 z-index를 쓰든)에 얹혀도 항상 최상단에 있어야 한다. */
.board-toggle-group {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 2147483000;
  display: flex;
  align-items: flex-end; /* 작은 위성이 큰 메인 FAB 바닥에 정렬 */
  gap: 8px;
  touch-action: none; /* 실제 모바일에서 버튼을 끌 때 페이지 스크롤로 새지 않는다. */
}

/* 드래그 중에는 버튼의 hover 확장을 멈춰 묶음의 경계가 움직이지 않게 한다. */
.board-toggle-group--dragging,
.board-toggle-group--dragging .board-fab {
  cursor: grabbing;
}

.board-toggle-group--dragging .board-fab--main,
.board-toggle-group--dragging .board-fab--main:hover {
  width: 52px;
  border-radius: 50%;
  padding: 0;
  background-image: var(--fab-circle);
}

.board-toggle-group--dragging .board-fab__wordmark {
  max-width: 0;
  opacity: 0;
  margin-left: 0;
}

/* 원형 플로팅 버튼(ADR-0037) — TanStack Query Devtools식 FAB. rough 원 테두리(CHROME_CIRCLE)를
   background-image로 인라인 지정하고(rrb-rough-chrome), 실선 border는 지운다. */
.board-fab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background-color: #eef2ff;
  color: #312e81;
  cursor: pointer;
  transition: background-color 120ms ease, transform 120ms ease;
}

.board-fab:hover {
  transform: translateY(-1px);
}

/* 메인 FAB = 보드 열고 닫기. 평소엔 렌더 트리 글리프의 원(∅52px), hover/포커스 시 옆으로
   "render-board" 워드마크가 펼쳐지며 알약이 된다(ADR-0037 hover-expand, TanStack식).
   접힘/펼침에서 손그림 테두리 이미지를 원(--fab-circle)↔사각(--fab-pill)으로 갈아끼운다 —
   두 이미지 모두 인라인 CSS 변수로 내려온다(BoardOverlay.tsx). */
.board-fab--main {
  width: 52px;
  height: 52px;
  gap: 0;
  overflow: hidden;
  white-space: nowrap;
  background-image: var(--fab-circle);
  transition: width 170ms ease, border-radius 170ms ease, padding 170ms ease,
    background-color 120ms ease, transform 120ms ease;
}

.board-fab__tree {
  width: 23px;
  height: 23px;
  flex: none;
  display: block;
}

.board-fab__wordmark {
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  font-size: 15px;
  transition: max-width 170ms ease, opacity 150ms ease, margin-left 170ms ease;
}

/* 펼침 배경은 CHROME_BORDER(사각 rough 스케치)라, 완전 둥근 알약(radius 26px)으로 클립하면
   양 끝 캡에서 사각 테두리가 잘려 "열린" 것처럼 깨져 보인다 → 사각 스케치에 맞는 라운드 사각형
   (12px)으로 둬 테두리가 온전히 닫히게 한다(ADR-0037 후속 수정). */
.board-fab--main:hover,
.board-fab--main:focus-visible {
  width: 190px;
  border-radius: 12px;
  padding: 0 18px;
  background-image: var(--fab-pill);
}

.board-fab--main:hover .board-fab__wordmark,
.board-fab--main:focus-visible .board-fab__wordmark {
  max-width: 150px;
  opacity: 1;
  margin-left: 9px;
}

/* 열려 있는 동안은 눌린 상태를 채움으로 보여준다. */
.board-fab--open {
  background-color: #6366f1;
  color: #eef2ff;
}

/* 위성 = 요소 선택(픽) 모드 토글. 메인보다 작은 원, 마우스 포인터 아이콘. */
.board-fab--pick {
  width: 36px;
  height: 36px;
}

/* 전광판 토글도 픽 위성과 같은 작은 크롬으로 둔다. 켜짐 상태는 같은 강조색을 공유해
   "선택 보조 정보가 현재 표시되는가"만 짧게 전달한다. */
.board-fab--billboard {
  width: 36px;
  height: 36px;
}

/* 기본 상태에서 보조 기능은 28px 도구 버튼 하나에 접어, 3개 원형 FAB가 넓게 가로로
   퍼지는 것을 막는다. 열렸을 때만 위쪽으로 2개를 세로로 내보내 화면 모서리의 폭은 유지한다. */
.board-fab__tools {
  position: relative;
  display: flex;
  align-items: flex-end;
}

.board-fab--tools {
  width: 28px;
  height: 28px;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1;
}

.board-fab__tools-menu {
  position: absolute;
  right: -4px;
  bottom: calc(100% + 8px);
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px;
  border: 1px solid #cbd5e1;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.96);
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.18);
}

.board-fab__icon {
  width: 17px;
  height: 17px;
  display: block;
}

/* "요소 선택" 모드(ADR-0025 후속) — 켜져 있는 동안 계측 대상 앱 클릭이 정상 동작 대신 역방향
   인터랙션으로 소비된다(domInteraction.ts). 눌려 있는 상태를 채움으로 눈에 띄게 보여준다. */
.board-fab--pick-active {
  background-color: #6366f1;
  color: #eef2ff;
}

/* 선택 요소 전광판(ADR-0080): hover 중에는 "무엇을 고를지"만, 클릭으로 고정된 뒤에는
   AI용 복사를 제공한다. 패널/FAB 위치와 독립된 상단 중앙이라 이동 가능한 FAB의 측정·드래그
   범위를 오염시키지 않고, 호스트 앱 레이아웃에는 전혀 참여하지 않는다. */
.target-billboard {
  position: fixed;
  z-index: 2147483001;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  width: min(460px, calc(100vw - 32px));
  box-sizing: border-box;
  padding: 11px 12px 10px;
  border: 1px solid #c7d2fe;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.96);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.16);
  color: #182033;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

.target-billboard__heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.target-billboard--preview {
  pointer-events: none;
  border-color: #bfdbfe;
  background: rgba(239, 246, 255, 0.96);
}

.target-billboard__eyebrow {
  margin-bottom: 4px;
  color: #6366f1;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 11px;
  font-weight: 750;
  letter-spacing: 0.03em;
}

.target-billboard__drag {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  background: #fff;
  color: #64748b;
  cursor: grab;
  font-size: 17px;
  line-height: 1;
}

.target-billboard__drag:active {
  cursor: grabbing;
}

.target-billboard__drag:hover,
.target-billboard__drag:focus-visible {
  border-color: #818cf8;
  color: #4338ca;
}

.target-billboard__target {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 650;
  line-height: 1.45;
}

.target-billboard__hint {
  margin-top: 7px;
  color: #64748b;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 11px;
}

.target-billboard__identification {
  display: grid;
  gap: 2px;
  margin-top: 8px;
  padding: 6px 7px;
  border-radius: 6px;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 11px;
  line-height: 1.35;
}

.target-billboard__identification--assisted {
  background: #eff6ff;
  color: #1d4ed8;
}

.target-billboard__identification--ambiguous {
  background: #fff7ed;
  color: #b45309;
}

.target-billboard__actions {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 9px;
}

.target-billboard__copy,
.target-billboard__clear {
  border: 1px solid transparent;
  cursor: pointer;
  font-family: ui-sans-serif, system-ui, sans-serif;
}

.target-billboard__copy {
  padding: 5px 8px;
  border-radius: 6px;
  background: #4f46e5;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
}

.target-billboard__copy:hover,
.target-billboard__copy:focus-visible {
  background: #4338ca;
}

.target-billboard__clear {
  width: 26px;
  height: 26px;
  padding: 0;
  border-color: #cbd5e1;
  border-radius: 6px;
  background: #fff;
  color: #64748b;
  font-size: 20px;
  line-height: 1;
}

.target-billboard__clear:hover,
.target-billboard__clear:focus-visible {
  border-color: #818cf8;
  color: #4338ca;
}

body.rrb-dark-mode .target-billboard {
  border-color: #475569;
  background: rgba(15, 23, 42, 0.97);
  color: #e2e8f0;
}

body.rrb-dark-mode .target-billboard--preview {
  border-color: #334155;
  background: rgba(15, 23, 42, 0.97);
}

body.rrb-dark-mode .target-billboard__hint,
body.rrb-dark-mode .target-billboard__clear,
body.rrb-dark-mode .target-billboard__drag {
  color: #94a3b8;
}

body.rrb-dark-mode .target-billboard__identification--assisted {
  background: #172554;
  color: #bfdbfe;
}

body.rrb-dark-mode .target-billboard__identification--ambiguous {
  background: #431407;
  color: #fed7aa;
}

body.rrb-dark-mode .target-billboard__clear {
  border-color: #475569;
  background: #1e293b;
}

body.rrb-dark-mode .target-billboard__drag {
  border-color: #475569;
  background: #1e293b;
}

/* 픽 모드 중에는 계측 대상 앱 전체(패널 제외)에서 커서를 바꿔 "지금 클릭하면 요소가
   선택된다"는 걸 알려준다 — 브라우저 네이티브 "요소 검사"와 같은 관례. */
body.rrb-pick-mode {
  cursor: crosshair;
}

/* 도킹 패널(ADR-0025) — TanStack Query Devtools 패턴. 화면 하단에 고정폭으로 붙어, 계측
   대상 앱(위쪽 나머지 화면)은 패널이 열려 있는 동안에도 항상 보이고 조작 가능하다. 원래
   ADR-0020이 정한 "전체화면을 덮는" 모델은 보드가 열려 있는 동안 실제 앱과의 상호작용
   자체가 물리적으로 막혀 정방향/역방향 인터랙션(ADR-0024)의 전제와 충돌한다는 게 실측으로
   드러나 ADR-0025로 수정했다. */
.board-panel {
  position: fixed;
  z-index: 2147482999;
  background: #fff;
  box-shadow: 0 0 16px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
}

/* 도킹 방향(ADR-0040): 하단이면 화면 높이의 --rrb-panel-size 만큼, 좌/우면 너비 만큼 차지한다.
   변수는 BoardOverlay가 body에 vh/vw 단위로 내려주고, 없으면 CSS 기본값으로 폴백한다. */
.board-panel--bottom {
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--rrb-panel-size, 45vh);
  border-top: 1px solid #d0d3da;
}

.board-panel--top {
  top: 0;
  left: 0;
  right: 0;
  height: var(--rrb-panel-size, 45vh);
  border-bottom: 1px solid #d0d3da;
}

.board-panel--left {
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--rrb-panel-size, 40vw);
  border-right: 1px solid #d0d3da;
}

.board-panel--right {
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--rrb-panel-size, 40vw);
  border-left: 1px solid #d0d3da;
}

/* Document Picture-in-Picture 안에서는 별도 문서의 전체 viewport를 보드에 쓴다. */
.board-panel--detached {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
}
.board-panel--detached .board-panel__resize {
  display: none;
}

/* 선택한 실제 요소를 계속 볼 수 없는 넓은 대상일 때만 쓰는 최소 레일. 캔버스를 숨겨
   React Flow가 작은 viewport로 재측정/fitView하는 부작용도 피한다. */
.board-panel--rail {
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}
.board-panel--rail.board-panel--left,
.board-panel--rail.board-panel--right {
  width: 52px;
}
.board-panel--rail.board-panel--top,
.board-panel--rail.board-panel--bottom {
  height: 52px;
}
.board-panel__rail-button {
  width: 100%;
  height: 100%;
  border: 0;
  background: #4f46e5;
  color: #fff;
  cursor: pointer;
  font-size: 20px;
}

/* 리사이즈 핸들 — 패널의 "안쪽" 가장자리(하단=위, 좌=오른쪽, 우=왼쪽)에 얇게 얹는다. */
.board-panel__resize {
  position: absolute;
  z-index: 3;
  background: transparent;
  touch-action: none;
}

.board-panel__resize::after {
  content: '';
  position: absolute;
  background: #cbd2dc;
  border-radius: 3px;
  transition: background-color 120ms ease;
}

.board-panel__resize:hover::after {
  background: #6366f1;
}

.board-panel--bottom .board-panel__resize {
  top: 0;
  left: 0;
  right: 0;
  height: 8px;
  cursor: ns-resize;
}
.board-panel--top .board-panel__resize {
  bottom: 0;
  left: 0;
  right: 0;
  height: 8px;
  cursor: ns-resize;
}
.board-panel--top .board-panel__resize::after {
  left: 50%;
  bottom: 3px;
  transform: translateX(-50%);
  width: 42px;
  height: 3px;
}
.board-panel--bottom .board-panel__resize::after {
  left: 50%;
  top: 3px;
  transform: translateX(-50%);
  width: 42px;
  height: 3px;
}

.board-panel--left .board-panel__resize,
.board-panel--right .board-panel__resize {
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: ew-resize;
}
.board-panel--left .board-panel__resize {
  right: 0;
}
.board-panel--right .board-panel__resize {
  left: 0;
}
.board-panel--left .board-panel__resize::after,
.board-panel--right .board-panel__resize::after {
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 42px;
}
.board-panel--left .board-panel__resize::after {
  right: 3px;
}
.board-panel--right .board-panel__resize::after {
  left: 3px;
}

/* 도킹 위치 전환 컨트롤은 툴바와 겹치지 않는 자체 크롬 바로 분리한다. 어떤 도킹 방향에서도
   검색·필터의 첫 행이 가려지지 않고, 리사이즈 핸들과도 독립적이다. */
.board-panel__chrome {
  position: relative;
  z-index: 5;
  display: flex;
  flex: 0 0 42px;
  align-items: center;
  justify-content: flex-end;
  padding: 5px 10px;
  border-bottom: 1px solid #d0d3da;
  background: #fafafa;
  box-sizing: border-box;
}
.board-panel__dock {
  display: flex;
  gap: 2px;
  padding: 3px;
  background: #fff;
  border: 1px solid #d0d3da;
  border-radius: 8px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.14);
}


.board-panel__dock-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: #64748b;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.board-panel__dock-btn:hover {
  background: #eef2ff;
  color: #4f46e5;
}
.board-panel__dock-btn--active {
  background: #6366f1;
  color: #fff;
}

/* DomHighlightOverlay.tsx가 document.body에 포탈로 그리는 "React Scan 스타일" 테두리.
   pointer-events: none으로 실제 페이지 인터랙션을 절대 막지 않는다(architecture.md 원칙). */
.dom-highlight-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  pointer-events: none;
}

/* 클릭/더블클릭 하이라이트와 픽 모드 hover 프리뷰가 같은 표현을 공유한다(ADR-0043 후속): 손그림풍
   대각선 헷칭. 옅은 파란 채움(예전 __box)은 눈에 잘 안 띈다는 피드백으로, ADR-0038이 hover용으로
   도입한 헷칭을 클릭/이동 하이라이트도 함께 쓴다 — 일관성·가독성↑, 복잡도↓. 헷칭은 CSS
   repeating-linear-gradient라 요소 크기와 무관하게 늘어나고 JS 계산이 0이다. */
.dom-highlight-overlay__box,
.dom-highlight-overlay__hover {
  position: fixed;
  border: 2px solid #6366f1;
  border-radius: 5px;
  background-image: repeating-linear-gradient(
    45deg,
    rgba(99, 102, 241, 0.14),
    rgba(99, 102, 241, 0.14) 2px,
    transparent 2px,
    transparent 7px
  );
}

/* 클릭/더블클릭(확정 선택) — hover 프리뷰보다 한 단계 강조되게 은은한 외곽 글로우를 더한다. */
.dom-highlight-overlay__box {
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.22);
}

/* hover 프리뷰(ADR-0038) — 커서 아래 요소가 바뀔 때 위치/크기를 부드럽게 미끄러뜨린다. */
.dom-highlight-overlay__hover {
  transition: top 90ms ease-out, left 90ms ease-out, width 90ms ease-out, height 90ms ease-out;
}

/* --- 다크모드 (ADR-0027) ---
   <ReactFlow colorMode>가 xyflow 자체 크롬(Controls/MiniMap/Background/선택 아웃라인)은
   `.react-flow.dark` 클래스로 알아서 다시 칠해주지만, 커스텀 노드(GroupNode/ComponentNode)는
   자동 적용 대상이 아니라 여기서 직접 스코프한다. `.toolbar`/`.board-panel`처럼 `.react-flow`
   "밖"에 있는 요소는 그 클래스가 안 닿으므로, BoardOverlay.tsx가 이미 rrb-board-open/
   rrb-pick-mode에 쓰는 것과 같은 방식으로 document.body에 붙이는 rrb-dark-mode 클래스로
   스코프한다. */
.react-flow.dark .group-node {
  border-color: #475569;
  background: rgba(100, 116, 139, 0.12);
}

.react-flow.dark .group-node--pending {
  border-color: #334155;
  background: rgba(51, 65, 85, 0.15);
}

.react-flow.dark .group-node--collapsed {
  background: repeating-linear-gradient(
    135deg,
    rgba(100, 116, 139, 0.1),
    rgba(100, 116, 139, 0.1) 8px,
    rgba(100, 116, 139, 0.18) 8px,
    rgba(100, 116, 139, 0.18) 16px
  );
}

.react-flow.dark .group-node__label {
  color: #cbd5e1;
}

.react-flow.dark .group-node--pending .group-node__label {
  color: #64748b;
}

.react-flow.dark .group-node__count {
  color: #64748b;
}

.react-flow.dark .group-node--palette-0 { border-color: #818cf8; }
.react-flow.dark .group-node--palette-1 { border-color: #60a5fa; }
.react-flow.dark .group-node--palette-2 { border-color: #22d3ee; }
.react-flow.dark .group-node--palette-3 { border-color: #2dd4bf; }
.react-flow.dark .group-node--palette-4 { border-color: #a78bfa; }
.react-flow.dark .group-node--palette-5 { border-color: #e879f9; }
.react-flow.dark .group-node--palette-6 { border-color: #f472b6; }
.react-flow.dark .group-node--palette-7 { border-color: #fb7185; }

.react-flow.dark .component-node {
  background-color: #1e1b3a;
  color: #e0e7ff;
}

.react-flow.dark .component-node--host {
  background-color: #1e293b;
  color: #94a3b8;
}

.react-flow.dark .component-node--palette-0 { background-color: #818cf82a; }
.react-flow.dark .component-node--palette-1 { background-color: #60a5fa2a; }
.react-flow.dark .component-node--palette-2 { background-color: #22d3ee2a; }
.react-flow.dark .component-node--palette-3 { background-color: #2dd4bf2a; }
.react-flow.dark .component-node--palette-4 { background-color: #a78bfa2a; }
.react-flow.dark .component-node--palette-5 { background-color: #e879f92a; }
.react-flow.dark .component-node--palette-6 { background-color: #f472b62a; }
.react-flow.dark .component-node--palette-7 { background-color: #fb71852a; }

body.rrb-dark-mode .toolbar {
  background: #111827;
  border-color: #1f2937;
  color: #e5e7eb;
}

body.rrb-dark-mode .toolbar__count,
body.rrb-dark-mode .toolbar__search-count {
  color: #9ca3af;
}

body.rrb-dark-mode .toolbar__search {
  background: #1f2937;
  border-color: #374151;
  color: #e5e7eb;
}

body.rrb-dark-mode .toolbar__theme-toggle {
  background: #312e81;
  border-color: #818cf8;
  color: #eef2ff;
}

body.rrb-dark-mode .board-panel {
  background: #0b1120;
  border-top-color: #1f2937;
}

/* 도킹 컨트롤/리사이즈 그립 다크 대응(ADR-0040) */
body.rrb-dark-mode .board-panel--bottom {
  border-top-color: #1f2937;
}
body.rrb-dark-mode .board-panel--top {
  border-bottom-color: #1f2937;
}
body.rrb-dark-mode .board-panel--left {
  border-right-color: #1f2937;
}
body.rrb-dark-mode .board-panel--right {
  border-left-color: #1f2937;
}
body.rrb-dark-mode .board-panel__dock {
  background: #111827;
  border-color: #334155;
}
body.rrb-dark-mode .board-panel__dock-btn {
  color: #94a3b8;
}
body.rrb-dark-mode .board-panel__dock-btn:hover {
  background: #1e293b;
  color: #c7d2fe;
}
body.rrb-dark-mode .board-panel__resize::after {
  background: #475569;
}

body.rrb-dark-mode .board-fab__tools-menu {
  border-color: #403f39;
  background: rgba(30, 30, 28, 0.97);
}

body.rrb-dark-mode .toolbar__sticky-add {
  background: #422006;
  border-color: #a16207;
  color: #fef3c7;
}

.react-flow.dark .group-node__toggle {
  color: #94a3b8;
}

/* 스티키노트는 "포스트잇" 은유가 핵심이라 다크모드에서도 채도를 확 낮추지 않는다 — 어두운
   보드 위에서도 여전히 눈에 띄는 짙은 호박색 계열로만 조정한다. */
.react-flow.dark .sticky-note {
  background: #422006;
  border-color: #a16207;
}

.react-flow.dark .sticky-note__text {
  color: #fef3c7;
}

.react-flow.dark .sticky-note__text::placeholder {
  color: #d6a75c;
}

.react-flow.dark .sticky-note__delete {
  color: #d6a75c;
}

/* 컨텍스트 메뉴는 .react-flow "밖"(document.body 포탈)에 그려지므로 body 클래스로 스코프한다
   (toolbar/board-panel과 같은 이유). */
body.rrb-dark-mode .context-menu {
  background: #111827;
  border-color: #1f2937;
}

body.rrb-dark-mode .context-menu__item {
  color: #e5e7eb;
}

body.rrb-dark-mode .context-menu__item:hover {
  background: #1f2937;
}

/* ============================================================================
   시각 정체성: 도형 어휘(ADR-0028) + 손그림(ADR-0030) — 세션1(시각 언어) 소유 섹션.
   여기 아래는 세션1이 추가한다. rough 테두리/채움의 실제 이미지는 roughStyle.ts가 만들고
   (background-image 인라인), CSS는 형태(clip-path)·워드마크 폰트·볼펜 크롬 훅만 맡는다.
   ============================================================================ */

/* 라우트 진입점 6각형(ADR-0028). roughStyle.ts의 HEX_CUT_RATIO(0.18)와 폴리곤 x%가 반드시
   일치해야 한다(하나 바꾸면 둘 다). clip-path가 배경색(팔레트 틴트)까지 6각형으로 잘라, rough
   6각형 테두리와 형태가 맞물린다 — 사각 배경 위에 6각형 선만 얹혀 어긋나 보이는 걸 막는다. */
.component-node--route {
  clip-path: polygon(18% 0, 82% 0, 100% 50%, 82% 100%, 18% 100%, 0 50%);
}

/* 구조적 역할 경계(도형 어휘, ADR-0028) — 포탈/Suspense/에러 바운더리를 "이름표 붙은 프레임"으로
   두른다(사용자 결정: 아이콘 뱃지 대신 감싸는 영역을 박스로). 개발자가 아는 단어를 이름표로 써서
   범례가 필요 없다. 프레임은 React Flow 노드(type:'boundary', BoundaryFrame.tsx)라 그룹 자식으로
   깔리고, pointer-events:none으로 멤버 노드 클릭을 가로채지 않는다. 색: 포탈=teal, Suspense=violet,
   에러 바운더리=rose. */
.boundary-frame {
  position: relative;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: 1.5px dashed currentColor;
  border-radius: 10px;
  pointer-events: none;
}

/* 이름표는 프레임 "안쪽 상단"에 둔다(테두리를 걸치지 않음). boundaryFrames.ts의 TOP_INSET이
   그룹 라벨과 첫 멤버 사이 빈 띠에 이 이름표 자리를 확보하므로, 그룹 라벨(상단)과 세로로 분리돼
   가로 위치와 무관하게 겹치지 않는다. */
.boundary-frame__label {
  position: absolute;
  top: 3px;
  left: 8px;
  padding: 1px 7px;
  border-radius: 6px;
  background: #fff;
  color: inherit;
  font-family: ui-monospace, monospace;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
}

.boundary-frame--portal {
  color: #0d9488;
}
.boundary-frame--suspense {
  color: #7c3aed;
}
.boundary-frame--errorBoundary {
  color: #e11d48;
}

.react-flow.dark .boundary-frame__label {
  background: #0b1120;
}
.react-flow.dark .boundary-frame--portal {
  color: #2dd4bf;
}
.react-flow.dark .boundary-frame--suspense {
  color: #a78bfa;
}
.react-flow.dark .boundary-frame--errorBoundary {
  color: #fb7185;
}

/* 펼쳐진 그룹의 손그림 프레임(ADR-0030 축3, roughStyle.groupFrameImage) — CSS 실선/대시 대신
   rough 이미지가 테두리를 그리므로 CSS border를 지운다. 접힌/지도 모드와 가로:세로 비율이 8을
   넘는 프레임엔 이 클래스가 안 붙어 기존 대시 테두리를 유지한다. 특히 긴 프레임은 SVG를 억지로
   늘리지 않아 모서리 스케치가 중앙까지 내려오는 현상을 막는다(ADR-0095). */
.group-node--rough {
  border-color: transparent;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
}

/* 긴 그룹은 손그림 rectangle 대신 명확한 연필 실선으로 전환한다. 실제 화면 두께는 GroupNode의
   역-zoom outline이 맡고, 이 border는 outline 아래의 보조선으로 남긴다. rough 선 조각의 반복은
   이음새가 인공적으로 보여 채택하지 않는다. 점선은 캔버스의 점 격자와 섞여 프레임이 사라진 듯
   보이므로 접힌 그룹에만 남긴다. */
.group-node--rough-fallback {
  border-style: solid;
  border-width: 1.5px;
}

/* 손글씨 워드마크(ADR-0030 축1) — "render-board" 로고에만 손글씨체를 쓴다(다이어그램 라벨은
   식별자 판독성을 위해 모노스페이스 유지). OFL 오픈소스 웹폰트(Excalifont 등)를 self-host
   @font-face로 얹으면 이 스택 맨 앞에 자연히 들어온다 — 아직 정식 에셋을 못 넣어(오프라인,
   experiments/Virgil.woff2는 검증용 사본이라 재사용 금지) 지금은 시스템 손글씨 계열로 폴백한다. */
:root {
  --rrb-handwriting: 'Excalifont', 'Comic Sans MS', 'Segoe Print', 'Bradley Hand', 'Chalkboard SE', cursive;
}

.rrb-wordmark {
  font-family: var(--rrb-handwriting);
  font-weight: 600;
  letter-spacing: 0.2px;
}

/* 볼펜 세기 rough 크롬(ADR-0030 축2) — roughStyle.CHROME_BORDER를 background-image로 얹는
   버튼용. 손그림 테두리가 CSS 실선 border를 대신하므로 실선을 지우고, 크기가 제각각인 크롬을
   위해 정적 이미지를 늘린다(roughness 0.2라 늘려도 뭉개지지 않는다, "볼펜 끝으로 그린 버튼"). */
.rrb-rough-chrome {
  border-color: transparent;
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

/* 툴바(상단 UI)도 손그림 언어로 — 사용자 피드백 "위 UI 손그림 느낌으로". board-panel의 CSS
   변수(BoardOverlay.tsx)로 내려온 볼펜 rough 테두리를 툴바 버튼/검색박스에 얹고 CSS 실선
   border를 지운다. 버튼 각자의 배경 색(다크/메모 등)은 그대로 두고 테두리만 손그림으로 통일한다.
   shared Canvas.tsx의 툴바 JSX는 안 건드리고, 세션2가 추가한 버튼(잔상/일시정지)도 `.toolbar
   button` 선택자로 함께 손그림이 된다. 캔버스 안의 컨트롤/노드 버튼은 `.toolbar` 밑이 아니라 안 걸린다. */
.board-panel .toolbar button,
.board-panel .toolbar__search {
  border-color: transparent;
  background-image: var(--rrb-chrome-border-light);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

body.rrb-dark-mode .board-panel .toolbar button,
body.rrb-dark-mode .board-panel .toolbar__search {
  background-image: var(--rrb-chrome-border-dark);
}

/* 버튼 텍스트는 nowrap으로 한 줄 유지해 손그림 테두리(background-size:100% 100%)와
   어긋나지 않게 한다(스타일 깨짐 방지). */
.board-panel .toolbar button {
  white-space: nowrap;
}

/* 좁은 패널 오버플로 메뉴(사용자 리포트) — 좁으면 검색창 + "☰ 도구" 하나만 한 줄에 두고, 나머지
   컨트롤은 드롭다운에 접는다. 검색창은 이 모드에서 남는 폭을 채우되 줄어들 수 있게(min-width:0)
   해 절대 넘치지 않게 한다. 드롭다운은 툴바 기준 절대 배치라 세로 공간을 안 먹는다. */
.toolbar--compact {
  flex-wrap: nowrap;
  position: relative;
}
.toolbar--compact .toolbar__search {
  flex: 1 1 auto;
  min-width: 0;
}
.toolbar__overflow {
  position: relative;
  flex: 0 0 auto;
}
.toolbar__menu-btn {
  padding: 5px 10px;
  border: 1px solid #d0d3da;
  border-radius: 6px;
  background: #fff;
  color: #334155;
  font-size: 12px;
  cursor: pointer;
}
.toolbar__menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 12px;
  min-width: 190px;
  background: #fff;
  border: 1px solid #d0d3da;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.18);
}
.toolbar__menu button {
  width: 100%;
  text-align: left;
}
/* 드롭다운 안 버튼은 손그림 rough 크롬(.board-panel .toolbar button, 명시도 0,2,1)을 끈다 —
   width:100%로 통짜 stretch되면 손그림 테두리가 늘어나 깨져 보였다(사용자 리포트). 그 규칙을
   확실히 이기도록 같은 명시도(.board-panel .toolbar__menu button)로 두고 뒤에 선언한다. */
.board-panel .toolbar__menu button {
  background-image: none;
  border: 1px solid #d0d3da;
  border-radius: 6px;
}
body.rrb-dark-mode .board-panel .toolbar__menu button {
  border-color: #374151;
}
body.rrb-dark-mode .toolbar__menu-btn {
  background: #1f2937;
  border-color: #374151;
  color: #e5e7eb;
}
body.rrb-dark-mode .toolbar__menu {
  background: #0b1120;
  border-color: #1f2937;
}

/* ═══════════════════════════════════════════════════════════════════════════
   ADR-0032 — props 흐름 추적 + 변경 잔상(afterglow)
   이 파일은 다른 세션과 공유되므로, 이 기능의 스타일은 전부 이 구획 안에만 둔다.
   ═══════════════════════════════════════════════════════════════════════════ */

/* props 패널을 캔버스 우측에 절대 배치하려면 캔버스가 위치 기준이 돼야 한다. */
.canvas {
  position: relative;
}

/* props 패널(ADR-0032 1층) — 우선순위 정렬 스크롤 리스트. 캔버스 우측 상단에 겹쳐 뜬다. */
.props-panel {
  position: absolute;
  top: 12px;
  right: 12px;
  bottom: 12px;
  width: 280px;
  max-width: calc(100% - 24px);
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border: 1px solid #d0d3da;
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(15, 23, 42, 0.16);
  z-index: 20;
  font-size: 12px;
  overflow: hidden;
}

.props-panel__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid #e5e7eb;
  background: #f8fafc;
}
/* 헤더를 잡고 끌어 패널을 옮긴다(ADR-0051). touch-action:none으로 드래그 중 스크롤 간섭 차단. */
.props-panel__header--draggable {
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.props-panel__header--draggable:active {
  cursor: grabbing;
}
.props-panel__grip {
  color: #94a3b8;
  font-size: 13px;
  line-height: 1;
  flex-shrink: 0;
}
/* 좌하단 리사이즈 핸들(SW) — 우측 도킹 기본 위치에서 안쪽으로 넓히기 자연스럽게. */
.props-panel__resize {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  cursor: nesw-resize;
  touch-action: none;
  z-index: 1;
  background: linear-gradient(45deg, transparent 0 6px, #cbd5e1 6px 7px, transparent 7px 9px, #cbd5e1 9px 10px, transparent 10px);
}
.props-panel__resize:hover {
  background: linear-gradient(45deg, transparent 0 6px, #6366f1 6px 7px, transparent 7px 9px, #6366f1 9px 10px, transparent 10px);
}

.props-panel__title {
  font-family: ui-monospace, monospace;
  font-weight: 600;
  font-size: 13px;
  color: #0f172a;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

.props-panel__close {
  border: none;
  background: transparent;
  color: #64748b;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 4px;
}
.props-panel__close:hover {
  background: #e2e8f0;
  color: #0f172a;
}

.props-panel__empty {
  margin: 0;
  padding: 16px 12px;
  color: #94a3b8;
}

.props-panel__list {
  list-style: none;
  margin: 0;
  padding: 4px;
  overflow-y: auto;
  flex: 1;
}

/* 각 prop 행 — 추적 가능 행은 버튼처럼 클릭 가능, primitive는 흐리게(추적 불가). */
.props-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  padding: 5px 7px;
  font-size: 12px;
  font-family: ui-monospace, monospace;
  color: #1e293b;
  cursor: pointer;
}
.props-row:hover:not(:disabled) {
  background: #eef2ff;
  border-color: #c7d2fe;
}
/* primitive 등 추적 불가 행: 흐리게 + 클릭 불가(ADR-0032 "primitive는 아래로 흐리게"). */
.props-row--untrackable {
  cursor: default;
  color: #94a3b8;
}
/* 변경된 prop: 지속 마커(왼쪽 색 띠) — b1 이번 커밋 변경. */
.props-row--changed {
  border-left: 3px solid #f59e0b;
}
/* 추적 중인 prop: 강조 테두리. */
.props-row--tracked {
  background: #ecfeff;
  border-color: #22d3ee;
}

.props-row__key {
  font-weight: 600;
  color: #0f172a;
  flex-shrink: 0;
}
.props-row--untrackable .props-row__key {
  color: #94a3b8;
  font-weight: 500;
}
.props-row__value {
  color: #64748b;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.props-row__badge {
  flex-shrink: 0;
  font-size: 10px;
  font-family: system-ui, sans-serif;
  padding: 1px 5px;
  border-radius: 999px;
  line-height: 1.5;
}
.props-row__badge--changed {
  background: #fef3c7;
  color: #92400e;
}
.props-row__badge--tracked {
  background: #cffafe;
  color: #155e75;
}

.props-panel__footer {
  padding: 7px 10px;
  border-top: 1px solid #e5e7eb;
  background: #f8fafc;
  color: #475569;
  font-size: 11px;
}
.props-panel__footer code {
  font-family: ui-monospace, monospace;
  background: #e2e8f0;
  padding: 0 4px;
  border-radius: 3px;
}

/* 참조 추적 하이라이트(ADR-0032 3층) — 흐름의 끝점 노드는 청록 외곽선으로 표식(검색 매치/역방향
   착지와 구분). 하지만 "흐름" 자체는 아래 간선 강조가 주가 된다(레퍼런스의 "props는 간선에"). */
.component-node--tracked {
  outline: 2px dashed #06b6d4;
  outline-offset: 2px;
  border-radius: 4px;
}

/* Alt(⌥)-held 라이브 hover 동시 햇칭(ADR-0032 후속) — 실제 화면에서 커서 아래 요소를 hover하면
   대응 노드를 다이어그램에서도 인디고 햇칭으로 밝힌다(실제 요소 hover 햇칭과 짝). 대각선 줄무늬
   오버레이(::after)라 손그림 테두리/배경을 안 덮고, 텍스트도 읽힌다. 인디고 점선 outline으로
   위치를 또렷이(검색 매치=녹색, 추적=청록과 색 구분). 라이브 프리뷰라 sticky 착지(--highlighted
   rough 햇칭)보다 가볍게 CSS로만 그린다. */
.component-node--page-hovered {
  outline: 2px dashed #6366f1;
  outline-offset: 2px;
}
.component-node--page-hovered::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: repeating-linear-gradient(45deg, rgba(99, 102, 241, 0.26) 0 3px, transparent 3px 7px);
}

/* 참조가 실제로 지나간 경로 = 추적 서브체인의 기존 부모→자식 간선(ADR-0032). 청록 굵은 선 +
   흐르는 점선 애니메이션(animated)으로 "이 값이 여기로 흐른다"를 보여주고, 간선 라벨에 흐르는
   prop 이름을 얹는다. 그룹 횡단 간선(--cross-group) 위에 겹쳐도 이기도록 우선순위를 높인다. */
.react-flow__edge.edge-tracked .react-flow__edge-path {
  stroke: #06b6d4 !important;
  stroke-width: 3 !important;
  stroke-dasharray: 6 3 !important;
}
.react-flow__edge.edge-tracked .react-flow__edge-text {
  fill: #0e7490;
  font-family: ui-monospace, monospace;
  font-size: 11px;
  font-weight: 600;
}
.react-flow__edge.edge-tracked .react-flow__edge-textbg {
  fill: #ecfeff;
}
body.rrb-dark-mode .react-flow__edge.edge-tracked .react-flow__edge-text {
  fill: #67e8f9;
}
body.rrb-dark-mode .react-flow__edge.edge-tracked .react-flow__edge-textbg {
  fill: #083344;
}

/* 흐름 간선(ADR-0032) — 이 도구의 주(主) 동적 표현. 자식이 방금 props를 받은 간선을 부모→자식
   방향으로 "흐르게" 한다. animated=true가 React Flow의 움직이는 점선(source→target 방향)을 주므로,
   여기서는 색(따뜻한 주황)과 굵기만 얹으면 데이터가 트리를 타고 내려가는 게 보인다. 정밀 추적
   (청록 점선+라벨)과 색·역할이 구분된다. */
.react-flow__edge.edge-hot .react-flow__edge-path {
  stroke: #fb923c !important;
  stroke-width: 2.5 !important;
  filter: drop-shadow(0 0 2px rgba(251, 146, 60, 0.7));
}

/* 변경 노드 표식(ADR-0032) — 흐름의 주인공은 간선(위 edge-hot)이고, 노드는 "여기가 방금 바뀌었다"를
   알리는 은은한 보조 표식일 뿐이다(예전의 두꺼운 색 링에서 축소). heat 색(파랑=가끔→빨강=자주,
   ComponentNode가 인라인 color로 줌)을 얇은 링으로만 두르고, color transition으로 천천히 식는다. */
.component-node__afterglow {
  position: absolute;
  inset: -2px;
  border-radius: 5px;
  pointer-events: none;
  z-index: -1;
  border: 1.5px solid currentColor;
  transition: color 280ms linear, opacity 280ms linear;
}

/* 지도 모드 그룹 흐름(ADR-0032 Q2) — 그룹 안 노드들이 바쁘면 프레임이 heat 색으로 발광한다.
   box-shadow(색·굵기)는 GroupNode가 인라인으로 heat 색과 함께 주고, 여기서는 그 변화가 식으며
   부드럽게 이어지도록 transition만 건다("활동 기상도": 어느 도메인이 살아있나 한눈에). */
.group-node--flowing {
  transition: box-shadow 280ms linear;
}

/* 잔상/일시정지 툴바 버튼(ADR-0032) — 기존 toolbar 버튼과 같은 계열, 켜지면 강조. */
.toolbar__afterglow,
.toolbar__afterglow-pause {
  font-size: 12px;
  padding: 5px 10px;
  border: 1px solid #d0d3da;
  border-radius: 6px;
  background: #ffffff;
  color: #334155;
  cursor: pointer;
}
.toolbar__afterglow--on {
  background: #fff7ed;
  border-color: #fb923c;
  color: #9a3412;
}
.toolbar__afterglow-pause--on {
  background: #eff6ff;
  border-color: #60a5fa;
  color: #1e40af;
}

/* ── 다크모드 대응 (body.rrb-dark-mode / .react-flow.dark) ── */
body.rrb-dark-mode .props-panel {
  background: #0b1120;
  border-color: #1f2937;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
}
body.rrb-dark-mode .props-panel__header,
body.rrb-dark-mode .props-panel__footer {
  background: #111827;
  border-color: #1f2937;
}
body.rrb-dark-mode .props-panel__title {
  color: #e5e7eb;
}
body.rrb-dark-mode .props-panel__grip {
  color: #475569;
}
body.rrb-dark-mode .props-panel__resize {
  background: linear-gradient(45deg, transparent 0 6px, #334155 6px 7px, transparent 7px 9px, #334155 9px 10px, transparent 10px);
}
body.rrb-dark-mode .props-panel__resize:hover {
  background: linear-gradient(45deg, transparent 0 6px, #818cf8 6px 7px, transparent 7px 9px, #818cf8 9px 10px, transparent 10px);
}
body.rrb-dark-mode .props-panel__close {
  color: #94a3b8;
}
body.rrb-dark-mode .props-panel__close:hover {
  background: #1f2937;
  color: #e5e7eb;
}
body.rrb-dark-mode .props-row {
  color: #cbd5e1;
}
body.rrb-dark-mode .props-row:hover:not(:disabled) {
  background: #1e293b;
  border-color: #334155;
}
body.rrb-dark-mode .props-row__key {
  color: #e2e8f0;
}
body.rrb-dark-mode .props-row--tracked {
  background: #083344;
  border-color: #0e7490;
}
body.rrb-dark-mode .props-panel__footer {
  color: #94a3b8;
}
body.rrb-dark-mode .props-panel__footer code {
  background: #1f2937;
}
body.rrb-dark-mode .toolbar__afterglow,
body.rrb-dark-mode .toolbar__afterglow-pause {
  background: #1f2937;
  border-color: #374151;
  color: #e5e7eb;
}
body.rrb-dark-mode .toolbar__afterglow--on {
  background: #431407;
  border-color: #ea580c;
  color: #fed7aa;
}
body.rrb-dark-mode .toolbar__afterglow-pause--on {
  background: #172554;
  border-color: #2563eb;
  color: #bfdbfe;
}

/* Charcoal Sketchbook (ADR-0094) -------------------------------------------------
   다크 화면을 차가운 blue-black UI가 아니라 밤의 스케치북으로 만든다. 도메인색은
   색연필/마커처럼, 기본 크롬은 흑연·종이색처럼 읽히게 하고, 노드 tint는 11%만 사용해
   큰 트리에서도 색 안개가 생기지 않게 한다. 검색(초록)·추적(청록)·오류(산호)는 아래
   도메인 팔레트와 별도 상태 언어로 유지한다. */
.react-flow.dark {
  background: #171716;
}
.react-flow.dark .react-flow__background pattern {
  stroke: #30302c;
}
.react-flow.dark .react-flow__controls,
.react-flow.dark .react-flow__minimap {
  background: #1e1e1c;
  border-color: #403f39;
}
.react-flow.dark .react-flow__controls-button {
  background: #24231f;
  border-bottom-color: #403f39;
  color: #e8e2d5;
}
.react-flow.dark .react-flow__controls-button:hover {
  background: #302f2a;
}

.react-flow.dark .group-node {
  border-color: #706d65;
  background: rgba(184, 180, 168, 0.07);
}
.react-flow.dark .group-node--pending {
  border-color: #4c4a44;
  background: rgba(184, 180, 168, 0.035);
}
.react-flow.dark .group-node--collapsed {
  background: repeating-linear-gradient(
    135deg,
    rgba(184, 180, 168, 0.07),
    rgba(184, 180, 168, 0.07) 8px,
    rgba(184, 180, 168, 0.12) 8px,
    rgba(184, 180, 168, 0.12) 16px
  );
}
.react-flow.dark .group-node__label { color: #e8e2d5; }
.react-flow.dark .group-node--pending .group-node__label,
.react-flow.dark .group-node__count,
.react-flow.dark .group-node__toggle { color: #aaa69b; }

.react-flow.dark .group-node--palette-0 { border-color: #9ca9e8; }
.react-flow.dark .group-node--palette-1 { border-color: #83c5d6; }
.react-flow.dark .group-node--palette-2 { border-color: #83c9ae; }
.react-flow.dark .group-node--palette-3 { border-color: #a9bd7c; }
.react-flow.dark .group-node--palette-4 { border-color: #b89ad9; }
.react-flow.dark .group-node--palette-5 { border-color: #d99bb5; }
.react-flow.dark .group-node--palette-6 { border-color: #d89383; }
.react-flow.dark .group-node--palette-7 { border-color: #d6b36a; }

.react-flow.dark .component-node { background-color: #272622; color: #e8e2d5; }
.react-flow.dark .component-node--host { background-color: #24231f; color: #aaa69b; }
.react-flow.dark .component-node--palette-0 { background-color: #9ca9e81c; }
.react-flow.dark .component-node--palette-1 { background-color: #83c5d61c; }
.react-flow.dark .component-node--palette-2 { background-color: #83c9ae1c; }
.react-flow.dark .component-node--palette-3 { background-color: #a9bd7c1c; }
.react-flow.dark .component-node--palette-4 { background-color: #b89ad91c; }
.react-flow.dark .component-node--palette-5 { background-color: #d99bb51c; }
.react-flow.dark .component-node--palette-6 { background-color: #d893831c; }
.react-flow.dark .component-node--palette-7 { background-color: #d6b36a1c; }

body.rrb-dark-mode .toolbar,
body.rrb-dark-mode .board-panel__chrome,
body.rrb-dark-mode .board-panel__dock,
body.rrb-dark-mode .context-menu,
body.rrb-dark-mode .toolbar__menu,
body.rrb-dark-mode .props-panel__header,
body.rrb-dark-mode .props-panel__footer {
  background: #1e1e1c;
  border-color: #403f39;
  color: #e8e2d5;
}
body.rrb-dark-mode .board-panel,
body.rrb-dark-mode .props-panel {
  background: #171716;
  border-color: #403f39;
}
body.rrb-dark-mode .toolbar__count,
body.rrb-dark-mode .toolbar__search-count,
body.rrb-dark-mode .board-panel__dock-btn,
body.rrb-dark-mode .props-panel__grip,
body.rrb-dark-mode .props-panel__close,
body.rrb-dark-mode .props-panel__footer { color: #aaa69b; }
body.rrb-dark-mode .toolbar__search,
body.rrb-dark-mode .toolbar__menu-btn,
body.rrb-dark-mode .toolbar__afterglow,
body.rrb-dark-mode .toolbar__afterglow-pause,
body.rrb-dark-mode .props-panel__footer code {
  background: #2a2925;
  border-color: #4c4a44;
  color: #e8e2d5;
}
body.rrb-dark-mode .toolbar__theme-toggle {
  background: #393a57;
  border-color: #9ca9e8;
  color: #f1efff;
}
body.rrb-dark-mode .board-panel__dock-btn:hover,
body.rrb-dark-mode .context-menu__item:hover,
body.rrb-dark-mode .props-panel__close:hover {
  background: #302f2a;
  color: #e8e2d5;
}
body.rrb-dark-mode .context-menu__item,
body.rrb-dark-mode .props-panel__title,
body.rrb-dark-mode .props-row__key { color: #e8e2d5; }
body.rrb-dark-mode .props-row { color: #d1cdc1; }
body.rrb-dark-mode .props-row:hover:not(:disabled) {
  background: #2a2925;
  border-color: #4c4a44;
}
body.rrb-dark-mode .props-row--tracked {
  background: #1e3533;
  border-color: #83c9ae;
}
body.rrb-dark-mode .props-panel__resize {
  background: linear-gradient(45deg, transparent 0 6px, #706d65 6px 7px, transparent 7px 9px, #706d65 9px 10px, transparent 10px);
}
body.rrb-dark-mode .props-panel__resize:hover {
  background: linear-gradient(45deg, transparent 0 6px, #9ca9e8 6px 7px, transparent 7px 9px, #9ca9e8 9px 10px, transparent 10px);
}

.react-flow.dark .boundary-frame__label { background: #171716; }
.react-flow.dark .boundary-frame--portal { color: #83c9ae; }
.react-flow.dark .boundary-frame--suspense { color: #b89ad9; }
.react-flow.dark .boundary-frame--errorBoundary { color: #d89383; }

body.rrb-dark-mode .react-flow__edge.edge-parent-palette-0 .react-flow__edge-path { stroke: #9ca9e8; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-1 .react-flow__edge-path { stroke: #83c5d6; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-2 .react-flow__edge-path { stroke: #83c9ae; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-3 .react-flow__edge-path { stroke: #a9bd7c; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-4 .react-flow__edge-path { stroke: #b89ad9; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-5 .react-flow__edge-path { stroke: #d99bb5; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-6 .react-flow__edge-path { stroke: #d89383; }
body.rrb-dark-mode .react-flow__edge.edge-parent-palette-7 .react-flow__edge-path { stroke: #d6b36a; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-0 .react-flow__edge-path { stroke: #9ca9e8 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-1 .react-flow__edge-path { stroke: #83c5d6 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-2 .react-flow__edge-path { stroke: #83c9ae !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-3 .react-flow__edge-path { stroke: #a9bd7c !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-4 .react-flow__edge-path { stroke: #b89ad9 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-5 .react-flow__edge-path { stroke: #d99bb5 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-6 .react-flow__edge-path { stroke: #d89383 !important; }
.canvas.lineage-active .react-flow__edge.edge-lineage.edge-parent-palette-7 .react-flow__edge-path { stroke: #d6b36a !important; }
