/* SalesBoost RMP — 제품 화면 목업 공용 스킨
   solution/index.html과 solution/features/*가 같이 쓴다.
   목업은 랜딩과 기능 상세 페이지에 같은 마크업으로 등장하므로 파일을 나누지 말 것. */

/* ============================================================
   PRODUCT MOCKUPS — 제품 화면 목업 공용 스킨
   라이트(히어로)/다크(챕터) 양쪽에서 같은 마크업을 쓰기 위해
   색을 지역 변수로 뽑아두고 .mock-dark 로만 갈아끼운다.
   ============================================================ */
.mock,
.hero-visual {
    --m-surface: var(--white);
    --m-surface-2: var(--gray-50);
    --m-border: var(--gray-200);
    --m-text: var(--gray-900);
    --m-sub: var(--gray-sub);
    --m-track: var(--gray-100);
    --m-accent: var(--blue-1);
    --m-accent-soft: var(--blue-tint);
    --m-shadow: var(--shadow-3);
    font-family: var(--font-sans);
    /* **글자 선택을 막지 않는다**(2026-08-25 사용자: "이미지를 통으로 올려놓은 것처럼
       보인다 · 드래그·복사 가능하게"). 종전 `user-select: none` 은 가짜 제품 화면에서
       드래그가 지저분해지는 걸 막으려던 것인데, 부작용이 더 컸다 — 목업이 스크린샷으로
       읽혀 **진짜 화면이라는 인상 자체를 깎았다**. 2026-08-24 에 `.arch` 만 예외로
       뚫었다가(`feature.css`) 오늘 기본값을 뒤집었으므로 그 예외 규칙은 이제 무해한
       중복이다. 이미지 고스트 드래그는 이 속성과 무관하다(`draggable` 소관). */
    line-height: 1.45;
}

.mock-dark {
    --m-surface: #161D2C;
    --m-surface-2: #1C2434;
    --m-border: rgba(255, 255, 255, 0.10);
    --m-text: #FFFFFF;
    --m-sub: rgba(255, 255, 255, 0.55);
    --m-track: rgba(255, 255, 255, 0.09);
    --m-accent: var(--blue-glow);
    --m-accent-soft: rgba(127, 178, 255, 0.15);
    --m-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
}

/* 그리드/테이블 자식이 min-content로 페이지 폭을 밀지 않도록 */
.mock-phone,
.mock-panel,
.mock-browser { max-width: 100%; }

.hero-grid > *,
.chapter > *,
.mock-cand > *,
.mock-row > *,
.mock-app > *,
.mock-kpi { min-width: 0; }

.mock-main { overflow: hidden; }

/* --- 앱 화면 프레임 (기기 크롬 없이 화면만) --- */
.mock-phone {
    position: relative;
    width: 100%;
    max-width: 320px;
    background: var(--m-surface);
    border: 1px solid var(--m-border);
    border-radius: 16px;
    box-shadow: var(--m-shadow);
    overflow: hidden;
}

.mock-search {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0.5rem 0.75rem 0.65rem;
    padding: 0.5rem 0.7rem;
    background: var(--m-surface-2);
    border: 1px solid var(--m-border);
    border-radius: var(--radius-pill);
    font-size: 12px;
    color: var(--m-text);
    font-weight: 600;
    transition: border-color 260ms ease, background-color 260ms ease, box-shadow 260ms ease;
}

/* 검색하는 중이라는 신호는 테두리 한 겹까지만 — 더 주면 목업이 요란해진다 */
.mock-search.is-active {
    background: var(--m-surface);
    border-color: var(--m-accent);
    box-shadow: 0 0 0 3px var(--m-accent-soft);
}

.mock-search-text { color: var(--m-sub); font-weight: 500; }
.mock-search-text.is-filled { color: var(--m-text); font-weight: 600; }

.mock-search svg {
    width: 13px;
    height: 13px;
    color: var(--m-sub);
    flex: none;
}

.mock-list {
    padding: 0 0.5rem 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.mock-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.42rem 0.45rem;
    border-radius: 10px;
}

.mock-rank {
    width: 13px;
    flex: none;
    text-align: center;
    font-family: var(--font-number);
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
}

.mock-thumb {
    width: 30px;
    height: 40px;
    flex: none;
    border-radius: 3px;
    overflow: hidden;
    background: var(--m-track);
}

.mock-thumb img,
.mock-card-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.mock-row-body {
    flex: 1;
    min-width: 0;
}

.mock-row-title {
    font-size: 12px;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-row-meta {
    font-size: 10px;
    color: var(--m-sub);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-row.is-ad {
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.mock-ad-badge {
    flex: none;
    font-family: var(--font-number);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--m-accent);
    border: 1px solid var(--m-accent);
    border-radius: 4px;
    padding: 0 4px;
    line-height: 1.5;
}

/* --- 앱 탭 바 --- */
.mock-tabs {
    display: flex;
    gap: 1rem;
    padding: 0.85rem 0.9rem 0;
    border-bottom: 1px solid var(--m-border);
}

.mock-main > .mock-tabs { padding: 0; margin: 0.25rem 0 0.9rem; }

.mock-tab {
    font-size: 12px;
    font-weight: 600;
    color: var(--m-sub);
    padding-bottom: 0.5rem;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
}

.mock-tab.is-active {
    color: var(--m-text);
    border-bottom-color: var(--m-text);
}

/* --- 상품 캐러셀 (홈·카테고리 등 탐색 지면) --- */
.mock-carousel {
    padding: 0.85rem 0.9rem 0.5rem;
}

.mock-carousel + .mock-carousel { padding-top: 0.25rem; }

.mock-carousel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.mock-carousel-title {
    font-size: 13px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-carousel-more {
    font-size: 10px;
    color: var(--m-sub);
    white-space: nowrap;
}

.mock-cards {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.5rem;
}

.mock-card { min-width: 0; }

.mock-card-img {
    position: relative;
    aspect-ratio: 3 / 4;
    border-radius: 5px;
    margin-bottom: 0.4rem;
    overflow: hidden;
    background: var(--m-track);
}

.mock-card.is-ad .mock-card-img {
    box-shadow: 0 0 0 2px var(--m-accent);
}

.mock-card .mock-ad-badge {
    position: absolute;
    top: 5px;
    left: 5px;
    background: var(--m-surface);
}

.mock-card-title {
    font-size: 11px;
    font-weight: 600;
    color: var(--m-text);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* 이름 길이가 달라도 가격 줄이 어긋나지 않게 2줄 높이로 고정 */
    min-height: 2.9em;
}

.mock-card-price {
    font-family: var(--font-number);
    font-size: 10px;
    color: var(--m-sub);
}

/* 데스크톱 지면(브라우저 프레임)에서는 카드가 조금 더 크다 */
.mock-shelf {
    padding: 1rem 1.15rem 1.15rem;
}

.mock-shelf .mock-carousel { padding: 0 0 0.9rem; }
.mock-shelf .mock-list { padding: 0; }

/* --- 데스크톱 프레임 (대시보드/리포트) — 주소 표시줄 없이 화면만 --- */
.mock-browser {
    width: 100%;
    background: var(--m-surface);
    border: 1px solid var(--m-border);
    border-radius: 14px;
    box-shadow: var(--m-shadow);
    overflow: hidden;
}




.mock-app {
    display: grid;
    grid-template-columns: 130px 1fr;
}

/* --- 광고주 콘솔 (실제 rmp-ads-dashboard 캠페인 화면 기준) --- */
.mock-page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
}

.mock-page-sub {
    font-size: 10px;
    color: var(--m-sub);
    margin-top: 2px;
}

.mock-btn {
    flex: none;
    font-size: 10px;
    font-weight: 600;
    color: var(--white);
    background: var(--blue-1);
    border-radius: 6px;
    padding: 4px 9px;
    white-space: nowrap;
}

/* 실제 콘솔은 캠페인을 스위치로 켜고 끈다 — 상태가 곧 조작 지점이다 */
.mock-switch {
    display: inline-block;
    position: relative;
    width: 21px;
    height: 12px;
    flex: none;
    border-radius: var(--radius-pill);
    background: var(--m-track);
}

.mock-switch::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--white);
}

.mock-switch.is-on,
.mock-switch.is-paused { }

.mock-switch.is-on { background: var(--m-accent); }
.mock-switch.is-paused { background: #E8833A; }

.mock-switch.is-on::after,
.mock-switch.is-paused::after { left: 11px; }

/* 캠페인 표는 이름·상태·일 예산 3열이라 기본 첫 열 42%를 다시 잡는다 */
.mock-table.is-campaign th:nth-child(1),
.mock-table.is-campaign td:nth-child(1) { width: 34%; }

.mock-table.is-campaign th:nth-child(2),
.mock-table.is-campaign td:nth-child(2) { width: 24%; }

.mock-table.is-campaign th:nth-child(3),
.mock-table.is-campaign td:nth-child(3) { width: 18%; }

.mock-table.is-campaign th:nth-child(4),
.mock-table.is-campaign td:nth-child(4) { width: 24%; }

/* 목적은 보조 정보라 한 톤 낮춘다 */
.mock-table.is-campaign td:nth-child(2) {
    font-size: 10px;
    font-weight: 500;
    color: var(--m-sub);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 열이 셋이라 셀 좌우 여백을 줄여야 1024에서 금액이 넘치지 않는다 */
.mock-table.is-campaign th,
.mock-table.is-campaign td { padding: 0.3rem 0.25rem; }

/* 영문 헤더(Daily Budget)가 좁은 열을 넘지 않도록 */
.mock-table.is-campaign th { font-size: 9px; }


/* 생성 화면(좌)에 목록(우)이 아래쪽으로 걸쳐 흐르는 배치 */
.mock-stack {
    display: grid;
    grid-template-columns: 34% 1fr;
    width: 100%;
}

.mock-stack > * { grid-row: 1; }

.mock-stack-form {
    grid-column: 1;
    align-self: start;
    background: var(--m-surface);
    border: 1px solid var(--m-border);
    border-radius: 14px;
    box-shadow: var(--m-shadow);
}

.mock-stack-list {
    grid-column: 1 / -1;
    justify-self: end;
    align-self: start;
    width: 68%;
    margin-top: 8.5rem;
    z-index: 1;
}

.mock-stack-list .mock-app { grid-template-columns: 96px 1fr; }
.mock-stack-list .mock-side { padding-left: 0.35rem; padding-right: 0.35rem; }

/* 영문 라벨(Performance 등)이 잘리지 않는 최소 크기 */
.mock-stack-list .mock-nav-item { font-size: 10px; padding: 0.28rem 0.3rem; gap: 0.3rem; }

.mock-form { padding: 0.7rem 0.8rem 0.7rem; }

.mock-field + .mock-field { margin-top: 0.5rem; }

.mock-field-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
}

.mock-label {
    display: block;
    font-size: 9px;
    font-weight: 600;
    color: var(--m-sub);
    margin-bottom: 0.2rem;
}

.mock-count {
    font-family: var(--font-number);
    font-size: 9px;
    color: var(--m-sub);
}

.mock-input {
    display: block;
    padding: 4px 7px;
    border: 1px solid var(--m-border);
    border-radius: 5px;
    background: var(--m-surface-2);
    font-size: 10px;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-input.is-placeholder { color: var(--m-sub); }

.mock-pick {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding-top: 0.22rem;
    font-size: 10px;
    color: var(--m-text);
}

.mock-check {
    position: relative;
    width: 11px;
    height: 11px;
    flex: none;
    border: 1px solid var(--m-border);
    border-radius: 2px;
}

.mock-check.is-on {
    background: var(--m-accent);
    border-color: var(--m-accent);
}

.mock-check.is-on::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 45%;
    width: 3px;
    height: 6px;
    border: solid var(--m-surface);
    border-width: 0 1.6px 1.6px 0;
    transform: translate(-50%, -50%) rotate(45deg);
}

.mock-pick .mock-thumb { width: 17px; height: 23px; }

.mock-pick-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mock-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.45rem;
}

/* 과금 기준은 CPC 말고도 있으므로 고정 라벨이 아니라 고른 값처럼 보여준다 */
.mock-sel {
    /* inline-flex면 줄상자 leading이 붙어 옆 열 라벨과 높이가 어긋난다 */
    display: flex;
    width: fit-content;
    align-items: center;
    gap: 3px;
    height: 15px;
    margin-bottom: 0.2rem;
    padding: 0 4px 0 5px;
    border: 1px solid var(--m-border);
    border-radius: 4px;
    font-size: 9px;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
}

.mock-sel svg { width: 7px; height: 7px; flex: none; opacity: 0.55; }

/* 두 열의 입력이 같은 높이에서 시작하도록 라벨 줄을 맞춘다 */
.mock-cols .mock-label { height: 15px; line-height: 15px; }

.mock-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.22rem;
}

/* 영문 4개(Gender·Age·Region·Day)가 한 줄에 들어가야 폼 높이가 KO와 같아진다 */
.mock-chips .mock-chip { padding: 1px 6px; font-size: 9px; }

.mock-form-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.55rem;
    margin-top: 0.6rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--m-border);
    font-size: 10px;
    color: var(--m-sub);
}

.mock-side {
    border-right: 1px solid var(--m-border);
    padding: 0.7rem 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.mock-side-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--m-sub);
    padding: 0.2rem 0.5rem 0.4rem;
    text-transform: uppercase;
}

.mock-nav-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.32rem 0.5rem;
    border-radius: 6px;
    font-size: 11px;
    color: var(--m-sub);
}

.mock-nav-item svg {
    width: 12px;
    height: 12px;
    flex: none;
}

.mock-nav-item > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mock-nav-item.is-active {
    background: var(--m-accent-soft);
    color: var(--m-accent);
    font-weight: 600;
}

.mock-main {
    padding: 0.85rem 1rem 1rem;
    min-width: 0;
}

.mock-main-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.7rem;
}

.mock-main-title {
    font-size: 14px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-chip {
    font-size: 10px;
    color: var(--m-sub);
    border: 1px solid var(--m-border);
    border-radius: var(--radius-pill);
    padding: 1px 8px;
    white-space: nowrap;
}

.mock-kpis {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.45rem;
    margin-bottom: 0.8rem;
}

.mock-kpi {
    border: 1px solid var(--m-border);
    border-radius: 8px;
    padding: 0.45rem 0.55rem;
    min-width: 0;
}

.mock-kpi-label {
    font-size: 10px;
    color: var(--m-sub);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-kpi-value {
    font-family: var(--font-number);
    font-size: 15px;
    font-weight: 700;
    color: var(--m-text);
    letter-spacing: -0.02em;
}

.mock-kpi-delta {
    font-family: var(--font-number);
    font-size: 9px;
    font-weight: 600;
    color: #16A34A;
}

.mock-dark .mock-kpi-delta { color: #7BE495; }

/* --- 표 --- */
.mock-table {
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
}

.mock-table th {
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
    text-align: right;
    padding: 0.3rem 0.35rem;
    border-bottom: 1px solid var(--m-border);
    white-space: nowrap;
}

.mock-table th:first-child,
.mock-table td:first-child {
    text-align: left;
}

.mock-table td {
    /* 11px 이면 EN 상품명·지표명이 셀 밖으로 넘친다 — 내림 고정 */
    font-size: 10px;
    color: var(--m-text);
    text-align: right;
    padding: 0.36rem 0.35rem;
    border-bottom: 1px solid var(--m-track);
    white-space: nowrap;
}

.mock-table th:first-child,
.mock-table td:first-child {
    width: 42%;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-table td:first-child { font-weight: 600; }

.mock-table .num {
    font-family: var(--font-number);
    /* 세로로 쌓이는 숫자 열이라 tabular 다 — Inter 기본 figure 는 비례폭이라
       `241%` 가 `268%` 보다 좁아져 오른쪽 정렬인데도 왼쪽 끝이 행마다 흔들린다.
       (자릿수를 맞출 이웃이 없는 큰 단독 숫자는 반대로 비례폭을 쓴다) */
    font-variant-numeric: tabular-nums;
}

.mock-state {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
}

.mock-state::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #16A34A;
}

.mock-state.is-off::before { background: var(--m-border); }

/* --- 플레인 서피스 (검색 결과 등 실제 화면 조각) --- */
.mock-sheet {
    width: 100%;
    background: var(--m-surface);
    border: 1px solid var(--m-border);
    border-radius: 14px;
    box-shadow: var(--m-shadow);
    overflow: hidden;
}

.mock-sheet .mock-search { margin: 0.65rem 0.65rem 0.5rem; }
.mock-sheet .mock-list { padding: 0 0.45rem 0.55rem; }

.mock-sheet-meta {
    padding: 0 0.9rem 0.45rem;
    font-size: 10px;
    color: var(--m-sub);
}

/* 홈과 검색은 같은 폰의 두 화면이다 — 검색창은 홈에도 늘 떠 있고,
   결과는 검색창 아래(탭·진열)를 덮으며 올라온다 */
.mock-body { position: relative; }

.mock-results {
    position: absolute;
    inset: 0;
    z-index: 2;
    padding-top: 0.55rem;
    background: var(--m-surface);
    opacity: 0;
    transform: translateY(10px);
    pointer-events: none;
}

.mock-results.is-open {
    pointer-events: auto;
    opacity: 1;
    transform: none;
    transition: opacity 380ms ease-out, transform 580ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* 결과가 화면 아래로 계속된다는 것만 보여준다 — 맥락에 안 맞는 상품으로 칸을 채우지 않는다 */
.mock-skel {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0 0.5rem;
    -webkit-mask-image: linear-gradient(180deg, #000 25%, transparent 92%);
    mask-image: linear-gradient(180deg, #000 25%, transparent 92%);
}

.mock-skel-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.42rem 0.45rem;
}

.mock-skel-row span {
    width: 30px;
    height: 40px;
    flex: none;
    border-radius: 3px;
    background: var(--m-track);
}

.mock-skel-row b {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.mock-skel-row i { height: 7px; border-radius: 4px; background: var(--m-track); }
.mock-skel-row i:last-child { width: 52%; }

/* --- 상품 상세 (랜딩 결과) --- */
.mock-pdp {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    flex-direction: column;
    background: var(--m-surface);
    transform: translateY(100%);
    opacity: 0;
    pointer-events: none;
}

.mock-pdp.is-open {
    pointer-events: auto;
    transform: none;
    opacity: 1;
    transition: transform 620ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity 260ms ease-out;
}

.mock-pdp-bar {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex: none;
    padding: 0.6rem 0.7rem;
    border-bottom: 1px solid var(--m-border);
    font-size: 11px;
    font-weight: 600;
    color: var(--m-sub);
}

.mock-pdp-bar svg {
    width: 15px;
    height: 15px;
    flex: none;
    color: var(--m-text);
}

/* 폰 높이에 따라 이미지 칸이 납작해질 수 있어 contain으로 둔다.
   배경은 뉴트럴하게 — 그라디언트를 흉내내면 소재 경계에서 이음매가 보인다 */
.mock-pdp-img {
    flex: 1;
    min-height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem 0;
    background: var(--m-surface-2);
}

.mock-pdp-img img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 6px;
}

.mock-pdp-body {
    flex: none;
    padding: 0.7rem 0.85rem 0.55rem;
}

.mock-pdp-title {
    font-size: 13px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-pdp-meta {
    margin-top: 0.2rem;
    font-size: 10px;
    color: var(--m-sub);
}

.mock-pdp-price {
    margin-top: 0.45rem;
    font-family: var(--font-number);
    font-size: 16px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-pdp-cta {
    flex: none;
    margin: 0 0.7rem 0.75rem;
    padding: 0.6rem;
    border-radius: 9px;
    background: var(--m-accent);
    color: var(--m-surface);
    text-align: center;
    font-size: 12px;
    font-weight: 700;
}

/* --- 매체 브랜드 마크 (공식 CI) --- */
.mock-brand {
    display: inline-flex;
    flex: none;
}

.mock-brand svg {
    display: block;
    width: 17px;
    height: 17px;
}

.mock-brandbar {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0 0.2rem 0.5rem;
    font-size: 12px;
    font-weight: 700;
    color: var(--m-text);
}

/* --- 외부 매체 미니 카드 (히어로 우측, 보조) --- */
.mock-mini-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.3rem;
    padding: 0.4rem 0.45rem;
    font-size: 10px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-mini-foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.3rem;
    padding: 0.35rem 0.45rem 0.45rem;
    font-size: 9px;
    color: var(--m-sub);
}

/* 영문 `Sponsored`가 길어 1024에서 가격이 카드 밖으로 밀려나왔다 — 가격을 고정하고 라벨만 줄인다 */
.mock-mini-foot > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mock-mini-foot b { flex: none; }

.mock-mini-foot b {
    font-family: var(--font-number);
    font-weight: 600;
    color: var(--m-text);
}

.mock-mini .mock-avatar { width: 16px; height: 16px; }
.mock-mini .mock-brand svg { width: 14px; height: 14px; }
.mock-mini .mock-ad-badge { font-size: 8px; padding: 0 3px; }
.mock-mini.mock-biz { padding: 0 0.4rem 0.45rem; }
.mock-mini .mock-biz-board { padding: 0.3rem; gap: 0.3rem; border-radius: 7px; }
.mock-mini .mock-thumb { width: 22px; height: 28px; }
.mock-mini .mock-chat { padding: 0.45rem 0.1rem 0; gap: 0.42rem; }
.mock-mini .mock-chat-av { width: 16px; height: 16px; border-radius: 5px; }
.mock-mini .mock-chat-lines { gap: 3px; }
.mock-mini .mock-chat-lines i { height: 4px; }

/* --- SNS 피드 광고 (외부 매체) --- */
.mock-social-head {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.55rem 0.6rem;
}

.mock-avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    flex: none;
    background: linear-gradient(140deg, #F4A3C0, #F0C97A);
}

.mock-social-name {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.35;
}

.mock-social-name b {
    /* 11px 이면 390px EN 에서 계정명이 두 줄이 된다 — 내림 고정 */
    font-size: 10px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-social-name em {
    font-style: normal;
    font-size: 9px;
    color: var(--m-sub);
}

.mock-social-img {
    aspect-ratio: 4 / 5;
    background: var(--m-track);
}

.mock-social-img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.mock-social-cap {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0.65rem 0.45rem;
    font-size: 11px;
    font-weight: 600;
    color: var(--m-text);
}

.mock-social-cap b {
    font-family: var(--font-number);
    font-weight: 600;
    color: var(--m-sub);
}

.mock-social-cta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0.65rem;
    background: var(--m-surface-2);
    border-top: 1px solid var(--m-border);
    font-size: 11px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-social-cta svg {
    width: 12px;
    height: 12px;
    flex: none;
    color: var(--m-sub);
}

/* --- 채팅탭 비즈보드 (외부 매체) --- */
.mock-biz { padding: 0.5rem; }

.mock-biz-board {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.45rem;
    border-radius: 9px;
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.mock-chat {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.6rem 0.35rem 0.2rem;
}

.mock-chat-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.mock-chat-av {
    width: 21px;
    height: 21px;
    border-radius: 7px;
    flex: none;
    background: var(--m-track);
}

.mock-chat-lines {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.mock-chat-lines i {
    display: block;
    height: 5px;
    border-radius: 3px;
    background: var(--m-track);
}

.mock-chat-lines i:first-child { width: 38%; }
.mock-chat-lines i:last-child { width: 74%; }

/* --- AI 매칭 패널 --- */
.mock-panel {
    width: 100%;
    background: var(--m-surface);
    border: 1px solid var(--m-border);
    border-radius: 14px;
    box-shadow: var(--m-shadow);
    padding: 1rem 1.1rem 1.1rem;
}

.mock-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.7rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid var(--m-border);
}

.mock-panel-title {
    font-size: 12px;
    font-weight: 700;
    color: var(--m-text);
}

/* **`--font-number`를 쓰지 않는다.** 이 칩은 대부분 `평균 38ms`·`최근 14일`처럼
   한글과 숫자가 섞인 문자열이라, Inter 를 1순위로 두면 숫자만 Inter 로 뜨고 한글은
   Pretendard 로 떨어져 한 단어에 서체가 둘이 된다(2026-08-25). 데이터로 보여야 하는
   큰 숫자가 아니라 11px 라벨이므로 `--font-sans` 하나로 간다. */
.mock-latency {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--font-sans);
    font-size: 10px;
    font-weight: 600;
    color: var(--m-accent);
    background: var(--m-accent-soft);
    border-radius: var(--radius-pill);
    padding: 2px 8px;
    white-space: nowrap;
}

.mock-query {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 12px;
    color: var(--m-sub);
    margin-bottom: 0.65rem;
}

.mock-query b {
    color: var(--m-text);
    font-weight: 700;
}

.mock-cand {
    display: grid;
    grid-template-columns: 1fr 46px 58px 70px;
    align-items: center;
    gap: 0.5rem;
    padding: 0.42rem 0.5rem;
    border-radius: 8px;
    font-size: 11px;
    color: var(--m-text);
}

.mock-cand-head {
    font-size: 9px;
    font-weight: 600;
    color: var(--m-sub);
    padding-bottom: 0.15rem;
}

.mock-cand span:not(:first-child) {
    text-align: right;
    font-family: var(--font-number);
}

.mock-cand-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
}

.mock-cand.is-win {
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.mock-cand.is-out {
    opacity: 0.45;
}

.mock-bar {
    display: block;
    height: 5px;
    border-radius: 3px;
    background: var(--m-track);
    overflow: hidden;
}

.mock-bar i {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--m-accent);
}

/* --- 광고 구좌 덱(④) — 한 자리 뒤에 다음 순번이 대기한다 --- */
.slot-ctx {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.8rem;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid var(--m-border);
    font-size: 12px;
    font-weight: 600;
    color: var(--m-text);
}

.slot-ctx b {
    margin-left: auto;
    font-size: 10px;
    font-weight: 500;
    color: var(--m-sub);
}

.slot-deck-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2.6rem;
}

.slot-deck {
    position: relative;
    flex: none;
    width: 142px;
    height: 220px;
    transition: opacity 0.3s ease;
}

.slot-deck.is-reset { opacity: 0; }

/* 리셋 순간에만 트랜지션을 끈다 — 안 끄면 카드가 나간 경로를 거꾸로 되짚어 어색하다 */
.slot-deck.no-anim .slot-card,
.slot-deck.no-anim .slot-meta { transition: none; }

.slot-card {
    position: absolute;
    left: 10px;
    top: 10px;
    width: 122px;
    transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s ease;
}

/* 기본 클래스가 완성 상태다 — JS가 없거나 모션을 끄면 이 모습 그대로 남는다 */
.slot-card.d0     { transform: none;         opacity: 1; z-index: 3; }
.slot-card.d1     { transform: scale(.94);   opacity: 0; z-index: 2; }
.slot-card.d2     { transform: scale(.9);    opacity: 0; z-index: 1; }
.slot-card.is-gone { transform: translate(-14px, -10px) scale(.96); opacity: 0; z-index: 0; }

.slot-art {
    position: relative;
    aspect-ratio: 3 / 4;
    border-radius: 7px;
    overflow: hidden;
    background: var(--m-track);
}

.slot-art img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* 이름·가격은 구좌에 실제로 들어간 카드에서만 읽힌다 (칸은 늘 차지해 높이가 흔들리지 않게) */
.slot-meta {
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin-top: 1rem;
    line-height: 1.35;
    opacity: 0;
    transition: opacity 0.25s ease;
}

.slot-card.d0 .slot-meta { opacity: 1; }

.slot-meta b {
    display: block;
    font-size: 11px;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.slot-meta span {
    font-size: 10px;
    color: var(--m-sub);
}

/* 테두리는 카드가 아니라 구좌에 붙어 있다 — 점유자가 바뀌어도 자리는 그대로다 */
.slot-frame {
    position: absolute;
    left: 0;
    top: 0;
    width: 142px;
    height: 183px;
    border: 1px dashed var(--m-border);
    border-radius: 10px;
    z-index: 0;
}

/* 배지 문구가 곧 광고의 출처다 (Sponsored / AD / External AD) — 카드마다 다르므로 카드 안에 둔다 */
.slot-art .mock-ad-badge {
    position: absolute;
    top: 5px;
    left: 5px;
    background: var(--m-surface);
}

.slot-chain {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    list-style: none;
}

.slot-chain li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 12px;
    color: var(--m-sub);
    opacity: 0.45;
    transition: opacity 0.35s ease, color 0.35s ease;
}

.slot-chain li.is-on {
    opacity: 1;
    color: var(--m-text);
    font-weight: 600;
}

.slot-chain i {
    width: 8px;
    height: 8px;
    flex: none;
    border-radius: 2px;
    background: var(--m-accent);
}

.slot-chain i.is-s2 { opacity: 0.58; }
.slot-chain i.is-s3 { opacity: 0.3; }

/* 좁은 폭에서는 덱(158) + 체인이 한 줄에 안 들어가, 가운데 정렬이 왼쪽 밖으로 밀린다 */
@media (max-width: 520px) {
    .slot-deck-row {
        flex-direction: column;
        gap: 1.1rem;
    }
}

/* --- 협력 광고 흐름(⑤) — 캠페인 하나가 플랫폼 밖으로 나갔다가 고객으로 돌아온다 --- */
/* 안(캠페인·상품 페이지)과 밖(외부 매체)을 두 구역으로 갈라 놓고 화살표가 그 경계를 두 번 넘는다.
   캠페인 설정이 상품 페이지와 같은 상자 안에 있어야 "우리 쪽"이라는 게 읽힌다 */
.coop-flow {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 148px) 66px minmax(0, 148px);
    grid-template-rows: auto auto auto;
    justify-content: center;
    align-items: center;
    justify-items: center;
    column-gap: 0.85rem;
    row-gap: 0.7rem;
}

.coop-zone {
    grid-row: 1 / 4;
    align-self: stretch;
    justify-self: stretch;
    z-index: 0;
    margin: -0.5rem;
    border-radius: 14px;
}

.coop-zone.is-in {
    grid-column: 1;
    border: 1px solid var(--m-border);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.012));
}

/* 밖은 점선 — 우리 지면이 아니라는 신호 */
.coop-zone.is-out {
    grid-column: 3;
    border: 1px dashed var(--m-border);
}

.coop-zcap {
    grid-row: 1;
    z-index: 1;
    justify-self: start;
    display: flex;
    align-items: center;
    gap: 0.34rem;
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
    white-space: nowrap;
}

.coop-zcap.is-in { grid-column: 1; }
.coop-zcap.is-out { grid-column: 3; }

.coop-zcap::before {
    content: "";
    width: 6px;
    height: 6px;
    flex: none;
    border-radius: 50%;
    background: var(--m-accent);
}

.coop-zcap.is-out::before { background: var(--m-sub); }

.coop-node { display: contents; }

/* 구역 라벨이 이미 안/밖을 말하므로 데스크톱에서는 카드별 캡션을 쓰지 않는다 */
.coop-node > .coop-cap { display: none; }

.coop-node[data-node="0"] > .coop-body { grid-area: 2 / 1; }
.coop-node[data-node="2"] > .coop-body { grid-area: 3 / 1; }
.coop-node[data-node="1"] > .coop-body { grid-area: 2 / 3 / 4 / 4; }
.coop-link[data-link="0"] { grid-area: 2 / 2; }
.coop-link[data-link="1"] { grid-area: 3 / 2; }

.coop-body {
    position: relative;
    z-index: 1;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.42rem;
}

.coop-cap {
    display: flex;
    align-items: flex-start;
    gap: 0.32rem;
    width: 100%;
    font-size: 10px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--m-sub);
}

.coop-cap::before {
    content: "";
    width: 6px;
    height: 6px;
    flex: none;
    margin-top: 0.28rem;
    border-radius: 50%;
    background: var(--m-accent);
}

.coop-node.is-out .coop-cap::before { background: var(--m-sub); }

.coop-card {
    position: relative;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    background: var(--m-surface);
    overflow: hidden;
}

.coop-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.35rem;
    padding: 0.35rem 0.5rem;
    border-bottom: 1px solid var(--m-border);
    background: var(--m-surface-2);
    font-size: 10px;
    font-weight: 700;
    color: var(--m-text);
}

/* 같은 플랫폼 내부라도 캠페인은 SalesBoost 콘솔에서, 유입은 매체 자기 지면으로 온다.
   앞은 액센트 톤 + 우리 심볼, 뒤는 담백한 상세 페이지 헤더로 갈라 놓는다 */
.coop-console .coop-bar { background: var(--m-accent-soft); }

.coop-brandline { display: flex; align-items: center; gap: 0.32rem; min-width: 0; }

.coop-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    flex: none;
    border-radius: 4px;
    background: var(--white);
}

.coop-mark img { display: block; width: 10px; height: 10px; object-fit: contain; }

.coop-back { display: flex; align-items: center; gap: 0.26rem; min-width: 0; color: var(--m-sub); }
.coop-back svg { width: 11px; height: 11px; flex: none; }

.coop-live {
    display: inline-flex;
    align-items: center;
    gap: 0.22rem;
    font-size: 8px;
    font-weight: 600;
    color: var(--m-accent);
}

.coop-live::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--m-accent);
}

.coop-camp-body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
}

.coop-item { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }

.coop-thumb {
    width: 26px;
    height: 26px;
    flex: none;
    border-radius: 6px;
    overflow: hidden;
    background: var(--m-track);
}

.coop-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }

.coop-item-name { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }

.coop-item-name b {
    font-size: 10px;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.coop-item-name em { font-style: normal; font-size: 9px; color: var(--m-sub); }

.coop-switch { display: flex; align-items: center; gap: 0.38rem; font-size: 9px; color: var(--m-text); }

.coop-switch i {
    position: relative;
    width: 19px;
    height: 11px;
    flex: none;
    border-radius: 6px;
    background: var(--m-accent);
}

.coop-switch i::after {
    content: "";
    position: absolute;
    top: 1.5px;
    right: 1.5px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--m-surface);
}

.coop-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.35rem;
    font-size: 9px;
    color: var(--m-sub);
}

.coop-row b {
    font-family: var(--font-number);
    font-size: 10px;
    font-weight: 600;
    color: var(--m-text);
}

.coop-feed-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.3rem;
    padding: 0.35rem 0.4rem;
}

.coop-feed-head .mock-avatar { width: 17px; height: 17px; }
.coop-feed-head .mock-brand svg { width: 15px; height: 15px; }

.coop-shot { position: relative; aspect-ratio: 1 / 1; background: var(--m-track); }
.coop-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.coop-shot .mock-ad-badge { position: absolute; top: 5px; left: 5px; background: var(--m-surface); }

/* 같은 사진이라도 지면이 다르다 — 피드 소재는 4:5 세로, 상품 페이지 대표 이미지는 4:3 가로 */
.coop-node.is-out .coop-shot { aspect-ratio: 4 / 5; }
.coop-land .coop-shot { aspect-ratio: 4 / 3; }

.coop-feed-foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.3rem;
    padding: 0.35rem 0.45rem 0.4rem;
    font-size: 9px;
    color: var(--m-sub);
}

.coop-feed-foot b { font-size: 9px; font-weight: 600; color: var(--m-accent); }

.coop-chans { display: flex; align-items: center; gap: 0.3rem; }

.coop-chans em {
    font-style: normal;
    font-size: 9px;
    color: var(--m-sub);
    white-space: nowrap;
}

.coop-chans .mock-brand svg { width: 15px; height: 15px; }

.coop-land-body { display: flex; flex-direction: column; gap: 2px; padding: 0.45rem 0.5rem 0.1rem; line-height: 1.3; }

.coop-land-body b {
    font-size: 10px;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.coop-land-body span { font-size: 8px; color: var(--m-sub); }

.coop-land-body em {
    font-style: normal;
    font-family: var(--font-number);
    font-size: 11px;
    font-weight: 700;
    color: var(--m-text);
}

.coop-buy {
    margin: 0.4rem 0.5rem 0.5rem;
    padding: 0.34rem;
    border-radius: 7px;
    background: var(--m-accent);
    color: var(--m-surface);
    text-align: center;
    font-size: 9px;
    font-weight: 700;
}

/* --- 경계를 넘는 두 화살표 --- */
/* 얇은 트랙 위에 굵은 선이 차오른다 — 트랙과 선의 두께 차이가 곧 무게감이다 */
.coop-link {
    position: relative;
    z-index: 1;
    width: 66px;
    height: 3px;
}

/* 아직 흐르지 않은 경로는 실선이 아니라 점선이다 — 1px 실선은 그냥 줄로 보인다 */
.coop-link::before {
    content: "";
    position: absolute;
    left: 0;
    right: 8px;
    top: 50%;
    height: 2px;
    margin-top: -1px;
    opacity: 0.42;
    background-image: radial-gradient(circle, var(--m-sub) 0 1px, transparent 1.2px);
    background-size: 6px 2px;
    background-repeat: repeat-x;
}

.coop-link.is-back::before { left: 8px; right: 0; }

.coop-link em {
    position: absolute;
    left: 50%;
    bottom: 100%;
    transform: translateX(-50%);
    margin-bottom: 0.4rem;
    font-style: normal;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--m-sub);
    white-space: nowrap;
}

/* 끝으로 갈수록 짙어지는 그라디언트 + 번짐 — 단색 막대가 주는 가벼움을 없앤다 */
.coop-link i {
    position: absolute;
    left: 0;
    right: 8px;
    top: 0;
    bottom: 0;
    border-radius: 2px 0 0 2px;
    background: linear-gradient(90deg, transparent, var(--m-accent) 42%, var(--m-accent));
    box-shadow: 0 0 9px rgba(127, 178, 255, 0.42), 0 0 20px rgba(127, 178, 255, 0.16);
    transform-origin: left center;
    transition: transform 0.85s cubic-bezier(0.5, 0, 0.2, 1);
}

.coop-link.is-back i {
    left: 8px;
    right: 0;
    border-radius: 0 2px 2px 0;
    background: linear-gradient(270deg, transparent, var(--m-accent) 42%, var(--m-accent));
    transform-origin: right center;
}

/* 선으로 그린 꺾쇠는 얇아서 싸구려로 보인다 — 모서리를 둥글린 채워진 삼각으로 간다 */
.coop-arrow {
    position: absolute;
    right: -3px;
    top: 50%;
    width: 14px;
    height: 14px;
    transform: translateY(-50%);
    color: var(--m-accent);
}

.coop-link.is-back .coop-arrow {
    right: auto;
    left: -3px;
    transform: translateY(-50%) scaleX(-1);
}

/* 기본 상태가 완성 상태다 — JS가 없거나 모션을 끄면 세 단계 모두 켜진 채로 남는다 */
.coop-flow.is-anim .coop-node > .coop-cap,
.coop-flow.is-anim .coop-node > .coop-body {
    opacity: 0.32;
    transition: opacity 0.45s ease;
}

.coop-flow.is-anim .coop-node.is-on > .coop-cap,
.coop-flow.is-anim .coop-node.is-on > .coop-body { opacity: 1; }

.coop-flow.is-anim .coop-link i { transform: scaleX(0); }
.coop-flow.is-anim .coop-link.is-on i { transform: scaleX(1); }

.coop-flow.is-anim .coop-arrow {
    color: var(--m-sub);
    opacity: 0.42;
    transition: color 0.5s ease, opacity 0.5s ease;
}

.coop-flow.is-anim .coop-link.is-on .coop-arrow { color: var(--m-accent); opacity: 1; }

/* 소재가 도착하기 전까지 외부 매체 지면은 비어 있다 */
.coop-flow.is-anim .coop-node.is-out .coop-shot img,
.coop-flow.is-anim .coop-node.is-out .coop-shot .mock-ad-badge {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.coop-flow.is-anim .coop-node.is-out.is-on .coop-shot img,
.coop-flow.is-anim .coop-node.is-out.is-on .coop-shot .mock-ad-badge { opacity: 1; }

.coop-flow.is-anim .coop-node.is-out.is-on .coop-shot img {
    animation: coopSettle 640ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes coopSettle {
    from { transform: scale(1.06); }
    to   { transform: scale(1); }
}

.coop-flow.is-anim .coop-node.is-out .coop-shot::after {
    content: "";
    position: absolute;
    inset: 9px;
    border: 1px dashed var(--m-border);
    border-radius: 6px;
    transition: opacity 0.3s ease;
}

.coop-flow.is-anim .coop-node.is-out.is-on .coop-shot::after { opacity: 0; }

.coop-flow.is-anim { transition: opacity 0.3s ease; }
.coop-flow.is-reset { opacity: 0; }

/* 리셋 순간에만 트랜지션을 끈다 — 안 끄면 켜진 순서를 거꾸로 되짚는다.
   미디어쿼리와 마찬가지로 .no-anim 자체는 특이도를 올려주지 않는다:
   위쪽 .is-anim 규칙과 선택자 길이를 맞춰야 실제로 이긴다 */
.coop-flow.no-anim .coop-node > .coop-cap,
.coop-flow.no-anim .coop-node > .coop-body,
.coop-flow.no-anim .coop-link i,
.coop-flow.no-anim .coop-arrow,
.coop-flow.no-anim .coop-node.is-out .coop-shot img,
.coop-flow.no-anim .coop-node.is-out .coop-shot::after,
.coop-flow.no-anim .coop-node.is-out .coop-shot .mock-ad-badge { transition: none; }

/* 착지는 얇은 링이 아니라 번짐으로 — 링만 남기면 값싸 보인다 */
.coop-card.is-land::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 10px;
    pointer-events: none;
    animation: coopLand 1400ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes coopLand {
    0%   { box-shadow: 0 0 0 0 rgba(127, 178, 255, 0.5), 0 0 20px 2px rgba(127, 178, 255, 0.42); }
    55%  { box-shadow: 0 0 0 9px rgba(127, 178, 255, 0.05), 0 0 34px 6px rgba(127, 178, 255, 0.18); }
    100% { box-shadow: 0 0 0 15px rgba(127, 178, 255, 0), 0 0 34px 6px rgba(127, 178, 255, 0); }
}

/* 소재가 날아가는 타일. 그림자 두 겹 + 액센트 번짐 + 안쪽 헤어라인으로 두께를 만든다 */
.coop-fly {
    position: absolute;
    z-index: 6;
    top: 0;
    left: 0;
    width: 46px;
    height: 46px;
    opacity: 0;
    pointer-events: none;
    filter: drop-shadow(0 3px 7px rgba(4, 8, 18, 0.5)) drop-shadow(0 14px 26px rgba(4, 8, 18, 0.45)) drop-shadow(0 0 12px rgba(127, 178, 255, 0.28));
}

.coop-fly-inner {
    position: relative;
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 11px;
    overflow: hidden;
    background: var(--m-surface);
}

.coop-fly-inner::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}

.coop-fly img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* 소재 배달(사각 타일)과 고객 유입(사람)이 같은 모양이면 두 방향이 구분되지 않는다.
   돌아오는 쪽은 사람 하나 + 뒤따르는 점 둘로, 더 빠르고 얕은 곡선으로 들어온다 */
.coop-visitor {
    position: absolute;
    z-index: 6;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin: -11px 0 0 -11px;
    border-radius: 50%;
    background: var(--m-surface);
    box-shadow: 0 0 0 1.5px var(--m-accent), 0 6px 14px rgba(4, 8, 18, 0.45), 0 0 14px rgba(127, 178, 255, 0.42);
    color: var(--m-accent);
    opacity: 0;
    pointer-events: none;
}

.coop-visitor svg { display: block; width: 12px; height: 12px; }

.coop-visitor.is-dot {
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -4px;
    background: var(--m-accent);
    box-shadow: 0 0 9px rgba(127, 178, 255, 0.65);
}

.coop-tap {
    position: absolute;
    z-index: 7;
    width: 26px;
    height: 26px;
    margin: -13px 0 0 -13px;
    border-radius: 50%;
    border: 2px solid var(--m-accent);
    background: rgba(127, 178, 255, 0.28);
    opacity: 0;
    pointer-events: none;
}

/* 링만으로는 "눌렸다"가 잘 안 읽힌다 — 히어로와 같은 커서를 같이 띄운다 */
.coop-cursor {
    position: absolute;
    z-index: 8;
    width: 17px;
    height: 17px;
    margin: -2px 0 0 -2px;
    opacity: 0;
    pointer-events: none;
    filter: drop-shadow(0 3px 6px rgba(16, 22, 35, 0.32));
}

.coop-cursor svg { display: block; width: 100%; height: 100%; }

/* 좁은 폭에서는 두 구역을 나란히 못 놓는다 — 세로로 세우고 구역은 카드별 캡션이 대신한다 */
@media (max-width: 520px) {
    .coop-flow {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: none;
        row-gap: 0.4rem;
    }

    .coop-zone,
    .coop-zcap { display: none; }

    .coop-flow .coop-node[data-node] > .coop-cap { display: flex; }

    .coop-flow .coop-node[data-node] > .coop-cap,
    .coop-flow .coop-node[data-node] > .coop-body,
    .coop-flow .coop-link[data-link] { grid-area: auto; }

    .coop-cap,
    .coop-body { max-width: 146px; }

    .coop-cap { align-self: start; margin-top: 0.3rem; }

    .coop-link {
        align-self: center;
        width: 3px;
        height: 28px;
        margin: 0.2rem 0;
    }

    .coop-link::before,
    .coop-link.is-back::before {
        left: 50%;
        right: auto;
        top: 0;
        bottom: 8px;
        width: 2px;
        height: auto;
        margin: 0 0 0 -1px;
        background-size: 2px 6px;
        background-repeat: repeat-y;
    }

    .coop-link i,
    .coop-link.is-back i {
        left: 0;
        right: 0;
        top: 0;
        bottom: 8px;
        border-radius: 2px 2px 0 0;
        background: linear-gradient(180deg, transparent, var(--m-accent) 42%, var(--m-accent));
        transform-origin: center top;
    }

    .coop-flow.is-anim .coop-link i { transform: scaleY(0); }
    .coop-flow.is-anim .coop-link.is-on i { transform: scaleY(1); }

    .coop-link em {
        left: 100%;
        bottom: auto;
        top: 50%;
        transform: translateY(-50%);
        margin: 0 0 0 0.45rem;
    }

    .coop-arrow,
    .coop-link.is-back .coop-arrow {
        right: 50%;
        left: auto;
        top: auto;
        bottom: -3px;
        transform: translateX(50%) rotate(90deg);
    }
}

/* --- 응답 전달 경계(⑥) — 우리 카드 / 플랫폼 카드 / 보안 카드, 셋은 따로 선다 --- */
.gov-wrap {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    width: 100%;
}

/* 두 카드 사이의 빈 자리가 곧 경계다 — 선을 그리지 않고 간격으로 말한다 */
.gov-flow {
    display: grid;
    grid-template-columns: 74px 26px minmax(0, 0.62fr) minmax(76px, 0.38fr) minmax(122px, 0.34fr);
    grid-template-rows: auto repeat(3, 30px);
    align-items: center;
    padding: 0.7rem 0;
    column-gap: 0;
    row-gap: 0.34rem;
}

.gov-card {
    grid-row: 1 / -1;
    align-self: stretch;
    justify-self: stretch;
    z-index: 0;
    margin: -0.7rem 0;
    border: 1px solid var(--m-border);
    border-radius: 12px;
    background: var(--m-surface);
    box-shadow: var(--m-shadow);
}

.gov-card.is-us { grid-column: 1 / 4; }
.gov-card.is-them { grid-column: 5; }

.gov-flow > *:not(.gov-card) {
    position: relative;
    z-index: 1;
}

.gov-flow [data-r="1"] { grid-row: 2; }
.gov-flow [data-r="2"] { grid-row: 3; }
.gov-flow [data-r="3"] { grid-row: 4; }

.gov-head {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    grid-row: 1;
    min-width: 0;
    padding: 0 0.75rem 0.45rem;
    font-size: 11px;
    font-weight: 700;
    white-space: nowrap;
    color: var(--m-text);
}

.gov-head.is-us { grid-column: 1 / 4; }
.gov-head.is-them { grid-column: 5; }

.gov-head em {
    font-style: normal;
    font-size: 9px;
    font-weight: 500;
    color: var(--m-sub);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 지면별 결과를 만들어 내는 자리 — 응답이 어디서 나오는지가 보여야 한다 */
.gov-engine {
    grid-column: 1;
    grid-row: 2 / 5;
    align-self: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3rem;
    width: 100%;
    padding-left: 0.6rem;
    text-align: center;
}

/* 큰 액센트 상자는 장난감처럼 보인다 — 뉴트럴 노드 + 캡션으로 낮춘다 */
.gov-engine u {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    background: var(--m-surface-2);
    color: var(--m-accent);
}

.gov-engine svg {
    width: 17px;
    height: 17px;
    flex: none;
}

.gov-engine b {
    font-size: 8px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--m-sub);
}

/* 엔진 한 곳에서 세 지면으로 갈라진다 — 행 높이가 고정이라 좌표를 계산해 둘 수 있다 */
.gov-fan {
    grid-column: 2;
    grid-row: 2 / 5;
    align-self: stretch;
    position: relative;
}

.gov-fan::before {
    content: '';
    position: absolute;
    left: 52%;
    top: 15px;
    bottom: 15px;
    border-left: 1px solid var(--m-border);
}

.gov-fan i {
    position: absolute;
    height: 0;
    border-top: 1px solid var(--m-border);
}

.gov-fan i:nth-of-type(1) { left: 0; right: 48%; top: 50%; }
.gov-fan i:nth-of-type(2) { left: 52%; right: 0; top: 15px; }
.gov-fan i:nth-of-type(3) { left: 52%; right: 0; top: 50%; }
.gov-fan i:nth-of-type(4) { left: 52%; right: 0; bottom: 15px; }

.gov-inv {
    grid-column: 3;
    display: flex;
    align-items: center;
    gap: 0.45rem;
    min-width: 0;
    height: 100%;
    padding: 0 0.75rem 0 0;
    font-size: 10px;
}

.gov-inv b {
    flex: 0 0 94px;
    min-width: 0;
    font-weight: 600;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 응답이 몇 위까지 실려 나가는지 — 지면마다 길이가 달라 건수가 눈으로 읽힌다 */
.gov-ranks {
    display: flex;
    gap: 3px;
    flex: 1;
    min-width: 0;
}

.gov-ranks span {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    border: 1px solid var(--m-border);
    border-radius: 4px;
    font-family: var(--font-number);
    font-size: 8px;
    font-weight: 700;
    color: var(--m-sub);
    opacity: 0.55;
    transition: opacity 300ms ease, border-color 300ms ease, color 300ms ease, background-color 300ms ease;
}

/* 켜진 칩 = 플랫폼이 실제로 쓴 순위. 응답은 다 나가지만 몇 개를 쓸지는 저쪽이 정한다 */
.gov-ranks span.is-on {
    border-color: var(--m-accent);
    color: var(--m-accent);
    background: var(--m-accent-soft);
    opacity: 1;
}

/* 끊긴 지면은 하나도 쓰지 않는다 */
[data-gov]:not(.is-open) .gov-ranks.is-cut span.is-on {
    border-color: var(--m-border);
    color: var(--m-sub);
    background: transparent;
    opacity: 0.55;
}

.gov-inv i {
    flex: none;
    width: 26px;
    text-align: right;
    /* 한글+숫자 혼용 라벨이라 `--font-number`(Inter 1순위)를 쓰면 숫자만 Inter,
       한글은 Pretendard 로 떨어진다(2026-08-25) — `--font-sans` 하나로 간다 */
    font-family: var(--font-sans);
    font-style: normal;
    font-size: 9px;
    font-weight: 700;
    color: var(--m-accent);
}

.gov-arrow {
    grid-column: 4;
    position: relative;
    height: 30px;
}

.gov-arrow i {
    position: absolute;
    left: 9px;
    right: 16px;
    top: 50%;
    height: 2px;
    margin-top: -1px;
    border-radius: 1px;
    background: var(--m-accent);
    box-shadow: 0 0 7px rgba(127, 178, 255, 0.35);
    transition: right 420ms cubic-bezier(0.4, 0, 0.2, 1), background-color 300ms ease, box-shadow 300ms ease;
}

.gov-tip,
.gov-stop {
    position: absolute;
    top: 50%;
    transition: opacity 300ms ease;
}

.gov-tip {
    right: 7px;
    width: 11px;
    height: 11px;
    margin-top: -5.5px;
    color: var(--m-accent);
}

.gov-stop {
    right: 8px;
    width: 13px;
    height: 13px;
    margin-top: -6.5px;
    color: var(--m-sub);
    opacity: 0;
}

/* 차단된 지면 — 선이 플랫폼 카드에 닿지 못하고 막힘 표시 앞에서 멈춘다 */
.gov-arrow.is-cut i {
    right: 28px;
    background: var(--m-sub);
    box-shadow: none;
}

.gov-arrow.is-cut .gov-tip { opacity: 0; }
.gov-arrow.is-cut .gov-stop { opacity: 1; }

.gov-state {
    grid-column: 5;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    width: fit-content;
    max-width: calc(100% - 1.5rem);
    height: 21px;
    margin: 0 0.75rem;
    padding: 0 0.5rem;
    border-radius: var(--radius-pill);
    font-size: 9px;
    font-weight: 600;
    white-space: nowrap;
    color: var(--m-accent);
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
    transition: color 300ms ease, background-color 300ms ease, box-shadow 300ms ease;
}

.gov-state::before {
    content: '';
    width: 5px;
    height: 5px;
    flex: none;
    border-radius: 50%;
    background: currentColor;
}

.gov-state b,
.gov-state i {
    font-style: normal;
    font-weight: 600;
}

.gov-state.is-cut {
    color: var(--m-sub);
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--m-border);
}

.gov-state.is-cut b { display: none; }

/* 플랫폼이 다시 열어 준 상태 — 끊겼던 선이 카드에 닿고 배지도 되살아난다 */
.gov-wrap.is-open .gov-arrow.is-cut i {
    right: 16px;
    background: var(--m-accent);
    box-shadow: 0 0 7px rgba(127, 178, 255, 0.35);
}

.gov-wrap.is-open .gov-arrow.is-cut .gov-tip { opacity: 1; }
.gov-wrap.is-open .gov-arrow.is-cut .gov-stop { opacity: 0; }

.gov-wrap.is-open .gov-state.is-cut {
    color: var(--m-accent);
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.gov-wrap.is-open .gov-state.is-cut b { display: inline; }
.gov-wrap.is-open .gov-state.is-cut i { display: none; }

/* 데이터 보안 — 위 흐름과 결이 다르므로 한 박자 띄우고 톤을 낮춘다.
   흐름 카드는 떠 있고(surface + shadow) 이 카드는 바닥에 붙어 있다(surface-2, 그림자 없음) */
/* 흐름 카드는 떠 있고(surface + shadow) 보안은 배경에 얹힌다 — 층이 다르면 결도 다르게 읽힌다 */
.gov-sec {
    margin-top: 1rem;
    padding: 1rem 0 0;
    border-top: 1px solid var(--m-border);
}

.gov-sec-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.45rem;
    margin-bottom: 0.6rem;
}

.gov-shield {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: none;
    border-radius: 8px;
    color: var(--m-accent);
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.gov-shield svg { width: 15px; height: 15px; }

.gov-sec-head > b {
    flex: 1;
    min-width: 0;
    font-size: 11px;
    font-weight: 700;
    color: var(--m-text);
}

.gov-certs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.22rem;
}

.gov-certs .mock-chip {
    font-size: 9px;
    padding: 1px 6px;
    font-family: var(--font-number);
}

/* 문장 대신 타일 넷 — 읽는 게 아니라 훑어보는 블록이다 */
.gov-facts {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.4rem;
}

.gov-fact {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 0.55rem 0.3rem;
    border: 1px solid var(--m-border);
    border-radius: 9px;
    text-align: center;
}

.gov-fact svg {
    width: 16px;
    height: 16px;
    flex: none;
    color: var(--m-accent);
}

.gov-fact b {
    font-size: 9px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--m-text);
}

/* 1200 아래에서는 응답 칩과 플랫폼 카드 부제를 접는다 — 넣을 자리가 없다 */
@media (max-width: 1200px) {
    .gov-head.is-them em { display: none; }
    .gov-ranks { display: none; }
    .gov-inv b { flex: 1; }
}

@media (max-width: 560px) {
    .gov-flow { grid-template-columns: 0 0 minmax(0, 1fr) 40px minmax(0, auto); }
    .gov-engine, .gov-fan { display: none; }
    .gov-head { padding: 0 0.55rem 0.4rem; }
    .gov-head em { display: none; }
    .gov-inv { padding: 0 0.55rem; }
    .gov-state { margin: 0 0.55rem; max-width: calc(100% - 1.1rem); }
    .gov-arrow i { left: 5px; right: 14px; }
    .gov-arrow.is-cut i { right: 22px; }
    .gov-tip { right: 5px; }
    .gov-stop { right: 6px; }
    .gov-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .gov-certs { flex: 1 0 100%; }
}

.mock-note {
    margin-top: 0.75rem;
    padding-top: 0.7rem;
    border-top: 1px solid var(--m-border);
    font-size: 10px;
    color: var(--m-sub);
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.mock-note svg {
    width: 12px;
    height: 12px;
    flex: none;
    color: var(--m-accent);
}

/* --- 차트 --- */
.mock-chart {
    width: 100%;
    height: auto;
    display: block;
}

.mock-chart .grid { stroke: var(--m-track); stroke-width: 1; }
.mock-chart .area { fill: var(--m-accent); opacity: 0.14; }
.mock-chart .line { fill: none; stroke: var(--m-accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.mock-chart .bar { fill: var(--m-accent); }
.mock-chart .bar-muted { fill: var(--m-track); }
.mock-chart .label { fill: var(--m-sub); font-size: 8px; font-family: var(--font-number); }


/* --- 조건 → 처리 규칙 목록 (기능 상세 챕터 ①) ---
   히어로가 이미 [신호 → 엔진 → 결과]를 보여주므로, 챕터에서 신호를 다시 나열하면
   더 빈약한 그림의 반복이 된다. 여기서는 **신호가 빠지거나 막혔을 때**를 다루고,
   자동으로 처리되는 것과 플랫폼이 정하는 것을 오른쪽 배지로 가른다. */
.rule { display: grid; gap: 0.5rem; }

.rule-row {
    display: grid;
    grid-template-columns: minmax(0, 0.92fr) 13px minmax(0, 1.08fr) auto;
    align-items: center;
    gap: 0.55rem;
    padding: 0.72rem 0.8rem;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    background: var(--m-surface-2);
}

.rule-row > b { min-width: 0; font-size: 12px; font-weight: 700; color: var(--m-text); }
.rule-row > i { min-width: 0; font-style: normal; font-size: 12px; line-height: 1.45; color: var(--m-sub); }

.rule-arrow {
    width: 13px;
    height: 1px;
    background: var(--m-border);
    position: relative;
}

.rule-arrow::after {
    content: '';
    position: absolute;
    right: -1px;
    top: 50%;
    transform: translateY(-50%);
    border: 3px solid transparent;
    border-right: 0;
    border-left-color: var(--m-border);
}

.rule-row > em {
    flex: none;
    padding: 0.18rem 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--m-track);
    font-style: normal;
    font-size: 10px;
    font-weight: 700;
    color: var(--m-sub);
    white-space: nowrap;
}

/* 플랫폼이 값을 정하는 행(`is-set`)과 이번 요청에서 실제로 뽑힌 값(`is-val`).
   둘 다 "기본값이 아니라 지금 정해진 것"이라 같은 액센트를 쓴다 */
.rule-row.is-set > em,
.rule-row.is-val > em { background: var(--m-accent-soft); color: var(--m-accent); }

/* --- AI 분석 모델 (기능 상세: 개인화 추천 챕터 ①) ---
   세 모델을 **세로로** 세우고, 왼쪽의 들어오는 데이터를 종류별로 각자에게 흘려보낸다
   (2026-08-24 사용자). 종전에는 `.rule.is-mdl` 한 줄짜리 표였는데, 표로는 셋이 동시에
   돈다는 것도 각자 다른 데이터를 본다는 것도 안 보였다.

   바깥은 3열이다 — `[레인 셋] [합류선] [관련도]`. 점수를 카드 안에 두면 "카드의 일부"로
   읽혀서 자기 상자로 떼어 냈고, 그 셋이 합류선을 타고 관련도 한 칸으로 모인다
   (2026-08-24 사용자: "별도 컨테이너 혹은 시각화로 · 각 점수가 종합되어 최종 점수가").

   정지 상태가 기본형이다 — 칩·점수·관련도가 처음부터 다 보이고, 아래 애니메이션은 그
   위에 덧입히기만 한다. 그래서 모든 @keyframes 는 **정지 상태에서 시작해 정지 상태로
   끝난다**. 하나라도 중간 값으로 끝나면 `.is-live` 를 떼는 순간 그림이 튄다. */
/* 표는 레인 **아래**다. 오른쪽에 세웠다가 걷어냈다 — `.chapter-visual` 안 패널이
   1440에서도 616px 뿐이라(챕터가 좌우 2단이다) 가로 3분할이 물리적으로 안 들어간다.
   레인 245px / 표 304px 로 쪼개지면서 EN 1024 에서 목업이 997px 까지 부풀었다. */
.mdl {
    display: grid;
    --mdl-rgap: 0.3rem;
}

.mdl-lanes { display: grid; grid-template-rows: repeat(3, 1fr); row-gap: var(--mdl-rgap); }

.mdl-lane {
    display: grid;
    grid-template-columns: minmax(0, 0.62fr) 30px minmax(0, 1.38fr);
    align-items: center;
    gap: 0.4rem;
}

/* 레인 셋 → 표로 내려가는 길 */
.mdl-down {
    position: relative;
    height: 14px;
    margin: 0.26rem 0 0.3rem;
}

.mdl-down::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 3px;
    border-left: 1px solid var(--m-border);
}

.mdl-down::after {
    content: '';
    position: absolute;
    left: calc(50% - 4px);
    bottom: 0;
    border: 4px solid transparent;
    border-bottom: 0;
    border-top-color: var(--m-border);
}

.mdl-src {
    display: block;
    min-width: 0;
    padding: 0.42rem 0.55rem;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    background: var(--m-surface-2);
}

.mdl-src b { display: block; font-size: 11px; font-weight: 600; color: var(--m-text); }

.mdl-src i {
    display: block;
    margin-top: 0.1rem;
    font-style: normal;
    font-size: 10px;
    line-height: 1.4;
    color: var(--m-sub);
}

/* 데이터가 지나가는 길. 선은 항상 있고, 지나가는 점(::after)만 애니메이션이다 */
.mdl-wire {
    position: relative;
    height: 1px;
    background: var(--m-border);
}

.mdl-wire::before {
    content: '';
    position: absolute;
    right: -1px;
    top: 50%;
    transform: translateY(-50%);
    border: 4px solid transparent;
    border-right: 0;
    border-left-color: var(--m-border);
}

.mdl-wire::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    width: 6px;
    height: 6px;
    margin: -3px 0 0 -3px;
    border-radius: 50%;
    background: var(--m-accent);
    opacity: 0;
}

.mdl-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 0.45rem;
    row-gap: 0.35rem;
    min-width: 0;
    padding: 0.42rem 0.55rem;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    background: var(--m-surface);
    box-shadow: 0 0 0 0 transparent;
}

/* 히어로 엔진 1층과 같은 아이콘 칩이라 확대로 읽힌다 — 카드가 낮아 한 치수 줄인다 */
.mdl-card .arch-ico {
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: center;
    width: 26px;
    height: 26px;
    border-radius: 8px;
}

.mdl-card .arch-ico svg { width: 15px; height: 15px; }

.mdl-card > b { grid-column: 2; grid-row: 1; min-width: 0; font-size: 12px; font-weight: 700; color: var(--m-text); }

/* 뽑아낸 값은 **한 요청의 것 아홉 개**가 한 번에 보인다. 두 개씩 세 벌을 겹쳐 놓고
   바퀴마다 갈아 끼웠다가 걷어냈다(2026-08-24 사용자) — ① 값이 바뀌는데 아래 채점 표는
   그대로라 앞뒤가 안 맞았고, ② 벌을 갈아 끼우는 순간 다음 벌이 한 번 번쩍였다.
   한 요청을 끝까지 따라가는 것이 이 페이지의 뼈대라, 값도 그 한 요청의 것으로 고정한다. */
.mdl-chips {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.mdl-chips i {
    padding: 0.12rem 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--m-accent-soft);
    font-style: normal;
    font-size: 10px;
    font-weight: 700;
    color: var(--m-accent);
    white-space: nowrap;
}

/* 결론은 **후보 상품 표**다. 모델마다 점수 하나를 달았더니 무슨 점수인지 알 수 없었고
   (2026-08-24 사용자), 점수는 결국 상품에 붙는 값이라 열로 세웠다 —
   세 모델 열의 평균이 관련도 열이고, 그 열이 곧 챕터 ③의 관련도다 */
.mdl-rank {
    min-width: 0;
    padding: 0.45rem 0.6rem 0.5rem;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    background: var(--m-surface-2);
}

.mdl-rk {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 42px 42px 42px 14px 50px;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0;
    font-size: 11px;
}

.mdl-rk.is-head {
    padding-bottom: 0.22rem;
    margin-bottom: 0.12rem;
    border-bottom: 1px solid var(--m-border);
    font-size: 9px;
    font-weight: 700;
    color: var(--m-sub);
}

.mdl-rk.is-head > span:not(:first-child) { text-align: center; }

.rk-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: var(--m-text);
}

.mdl-rk.is-win .rk-name { color: var(--m-accent); }

.rk-s {
    font-family: var(--font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: center;
    color: var(--m-sub);
}

/* 관련도만 결론이라 진하다 — 모델별 점수는 그 재료다 */
.rk-rel { font-weight: 800; color: var(--m-accent); }

.rk-arrow {
    position: relative;
    height: 1px;
    background: var(--m-border);
}

.rk-arrow::after {
    content: '';
    position: absolute;
    right: -1px;
    top: 50%;
    transform: translateY(-50%);
    border: 3px solid transparent;
    border-right: 0;
    border-left-color: var(--m-border);
}

/* 합류선 — 레인과 같은 3행(같은 gap)이라 가로 스텁이 각 점수 상자의 한가운데에 선다.
   세로 척추는 행마다 조각으로 그리고 gap 만큼 겹쳐 이어 붙인다(컨테이너 높이를 계산하지
   않아도 되는 대신, `.mdl-lanes` 와 gap 이 어긋나면 바로 틀어진다 — 같이 고칠 것) */
.mdl-sum {
    display: grid;
    grid-template-rows: repeat(3, 1fr);
    row-gap: var(--mdl-rgap);
}

.mdl-sum > i { position: relative; }

.mdl-sum > i::before {
    content: '';
    position: absolute;
    left: 0;
    width: 50%;
    top: 50%;
    border-top: 1px solid var(--m-border);
}

.mdl-sum > i::after {
    content: '';
    position: absolute;
    left: 50%;
    border-left: 1px solid var(--m-border);
}

.mdl-sum > i:nth-child(1)::after { top: 50%; bottom: calc(-1 * var(--mdl-rgap)); }
.mdl-sum > i:nth-child(2)::after { top: 0; bottom: 0; }
.mdl-sum > i:nth-child(3)::after { top: calc(-1 * var(--mdl-rgap)); bottom: 50%; }

/* 가운데 행만 오른쪽 끝까지 나가 관련도 칸으로 들어간다 */
.mdl-sum > i:nth-child(2)::before { width: 100%; }

/* 1080 아래에서는 폭을 카드 쪽으로 넘긴다 — 칩 두 개가 한 줄에 안 들어가면
   카드만 두 줄로 자라서 챕터 ①만 혼자 키가 커진다(EN 1024에서 실측) */
@media (min-width: 621px) and (max-width: 1080px) {
    .mdl-lane { grid-template-columns: minmax(0, 0.56fr) 26px minmax(0, 1.44fr); gap: 0.3rem; }
    .mdl-rk { grid-template-columns: minmax(0, 1fr) 38px 38px 38px 12px 44px; gap: 0.22rem; font-size: 10px; }
}

/* --- 움직임 --- 정지 그림 위에 덧입히는 **한 벌의 순서**다. 박자는 JS가 잡는다
   (`.mdl-lane` 에 `is-send → is-run → is-out`, 다 끝나면 `.mdl` 에 `is-join`).
   CSS 무한 애니메이션 여러 개를 서로 다른 delay 로 돌렸다가 갈아엎었다 —
   레인이 겹쳐 돌아 **화살표와 등장 순서가 어긋나 보이고 전체가 정신없었다**
   (2026-08-24 사용자). 지금은 한 번에 한 레인만 움직이고, 다음 레인은 앞 레인이
   끝나야 시작한다. 마지막 화면은 JS 가 2.6초 붙들었다가 처음부터 다시 돈다.

   전환(transition)이지 애니메이션이 아닌 것이 핵심이다 — 클래스가 없으면
   그냥 완성 상태라, 정지 그림이 자동으로 기본형이 된다. */
.mdl.is-live .mdl-chips i,
.mdl.is-live .rk-s {
    opacity: 0;
    transform: translateY(3px);
    transition: opacity 0.32s var(--ease-out), transform 0.32s var(--ease-out);
}

.mdl.is-live .mdl-lane.is-run .mdl-chips i {
    opacity: 1;
    transform: none;
    /* 값이 동시에 뜨면 "한 덩어리"로 보인다 — 차례로 하나씩.
       마지막 값(--j:2)이 0.32s + 0.32s = 0.64s 에 끝나므로 분석 박자는 0.7s 여야 한다 */
    transition-delay: calc(var(--j) * 0.16s);
}

/* 표 틀과 후보 이름은 계속 보이고, **모델 열이 하나씩** 채워진 뒤 마지막에 관련도가
   나온다 — "각 점수가 종합되어 최종 점수"가 순서로 읽히는 자리다.
   차례가 표로 넘어오는 순간(`is-rank`)에는 길과 표 테두리에 액센트가 든다 */
.mdl.is-live .mdl-down::before,
.mdl.is-live .mdl-down::after,
.mdl.is-live .mdl-rank { transition: border-color 0.3s var(--ease-out); }

.mdl.is-live.is-rank .mdl-down::before { border-left-color: var(--m-accent); }
.mdl.is-live.is-rank .mdl-down::after  { border-top-color: var(--m-accent); }
.mdl.is-live.is-rank .mdl-rank { border-color: var(--m-accent); }

.mdl.is-live .rk-s { transition-delay: calc(var(--r) * 0.07s); }
.mdl.is-live.is-c1 .rk-c1,
.mdl.is-live.is-c2 .rk-c2,
.mdl.is-live.is-c3 .rk-c3,
.mdl.is-live.is-rel .rk-rel { opacity: 1; transform: none; }

/* 지금 차례인 데이터 상자와 지금 도는 카드 — 각각 **한 번에 하나뿐**이다 */
.mdl.is-live .mdl-src,
.mdl.is-live .mdl-card {
    transition: border-color 0.3s var(--ease-out), background-color 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}

.mdl.is-live .mdl-lane.is-send:not(.is-run) .mdl-src {
    border-color: var(--m-accent);
    background: var(--m-accent-soft);
}

.mdl.is-live .mdl-lane.is-run:not(.is-done) .mdl-card {
    border-color: var(--m-accent);
    box-shadow: 0 0 0 3px var(--m-accent-soft);
}

/* 길을 건너는 점 — 자기 박자에 딱 한 번 지나간다 */
.mdl.is-live .mdl-lane.is-send .mdl-wire::after { animation: mdlSend 0.55s linear; }

@keyframes mdlSend {
    0%   { left: 0;    opacity: 0; }
    14%  { left: 8%;   opacity: 1; }
    82%  { left: 100%; opacity: 1; }
    100% { left: 100%; opacity: 0; }
}

/* 좁은 화면에서는 레인을 접는다 — 데이터가 위, 모델과 점수가 아래고 길도 세로로 눕는다.
   합류선은 뜻이 통하지 않으므로 끄고, 관련도를 마지막 줄에 가로로 깐다 */
@media (max-width: 620px) {
    .mdl-lanes { row-gap: 0.7rem; }
    .mdl-rk { grid-template-columns: minmax(0, 1fr) 34px 34px 34px 10px 42px; gap: 0.2rem; font-size: 10px; }

    .mdl-lane {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

    .mdl-wire {
        width: 1px;
        height: 22px;
        margin: 0 auto;
    }

    .mdl-wire::before {
        right: auto;
        left: 50%;
        top: auto;
        bottom: -1px;
        transform: translateX(-50%);
        border: 4px solid transparent;
        border-bottom: 0;
        border-top-color: var(--m-border);
        border-left-color: transparent;
    }

    .mdl-wire::after { top: 0; left: 50%; }
}

@media (max-width: 620px) {
    .mdl.is-live .mdl-lane.is-send .mdl-wire::after { animation-name: mdlSendV; }
}

@keyframes mdlSendV {
    0%   { top: 0;    opacity: 0; }
    14%  { top: 8%;   opacity: 1; }
    82%  { top: 100%; opacity: 1; }
    100% { top: 100%; opacity: 0; }
}


/* --- 로직 선택 (기능 상세: 개인화 추천 챕터 ②) ---
   세 판을 버리고 왔다. ① 60/22/18 가로 점유 막대 → A/B 트래픽 배분으로 읽힘.
   ② `[이름][설명][언제 쓰는지]` 행 나열 → 로직이 여럿이라는 것까지만 말함.
   ③ 조건 필터 + 성과 열 표 → **정보가 너무 많고 쓸데없는 시각화**(2026-08-24 사용자).
   지금은 **정적 스냅샷이 아니라 도는 루프**다 — 요청이 실시간으로 들어오고, 로직 넷의
   기대 수익이 그 자리에서 계산되고, 1위가 쓰이고, 그 결과가 다시 기대 수익을 갱신한다.
   사이클마다 요청이 바뀌면 1위도 바뀐다 — 그게 "요청마다 다른 로직"의 증명이다.
   정지 상태(모션 오프·화면 밖)가 곧 완성 상태이므로 HTML 이 첫 요청의 값을 그대로 갖는다. */
.mock-live { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 10px; font-weight: 700; color: var(--m-accent); }
.mock-live > i { width: 6px; height: 6px; border-radius: 50%; background: var(--m-accent); }
.rev.is-live .mock-live > i { animation: revPulse 1.6s ease-in-out infinite; }

.rev { display: grid; grid-template-columns: 20px minmax(0, 1fr); }
.rev > *:not(.rev-loop) { grid-column: 2; }

.rev-in {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 0.5rem;
    padding: 0.36rem 0.55rem;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    background: var(--m-surface-2);
}

.rev-in > b { font-size: 10px; font-weight: 700; color: var(--m-sub); white-space: nowrap; }

/* 요청 세 벌을 겹쳐 두지 않고 display 로 갈아 끼운다 — 페이드가 겹치면 교체 순간
   다음 벌이 번쩍인다(챕터 ① 에서 겪었다). 교체는 값이 다 지워진 박자에만 일어난다 */
.rev-chips { min-width: 0; }
.rev-chipset { display: none; flex-wrap: wrap; gap: 0.25rem; }
.rev-chipset.is-on { display: flex; }

.rev-chipset i {
    padding: 0.12rem 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--m-accent-soft);
    font-size: 10px;
    font-weight: 700;
    font-style: normal;
    color: var(--m-accent);
    white-space: nowrap;
}

.rev-down { position: relative; height: 13px; margin: 0.24rem 0; }

.rev-down::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 3px;
    border-left: 1px solid var(--m-border);
    transition: border-color 0.3s var(--ease-out);
}

.rev-down::after {
    content: '';
    position: absolute;
    left: calc(50% - 4px);
    bottom: 0;
    border: 4px solid transparent;
    border-bottom: 0;
    border-top-color: var(--m-border);
    transition: border-top-color 0.3s var(--ease-out);
}

.rev-down em { position: absolute; left: calc(50% - 2px); top: 0; width: 4px; height: 4px; border-radius: 50%; background: var(--m-accent); opacity: 0; }

.rev-tbl {
    padding: 0.38rem 0.55rem 0.44rem;
    border: 1px solid var(--m-border);
    border-radius: 9px;
    background: var(--m-surface);
    transition: border-color 0.3s var(--ease-out);
}

/* 순위가 바뀌는 것을 보여야 하므로 행은 flex 자식이고 JS 가 order 를 바꾼다.
   order 변경 자체는 애니메이션되지 않아서 FLIP(바뀌기 전 위치로 되돌린 뒤 transform 을 푼다)으로 옮긴다 */
.rev-rows { display: flex; flex-direction: column; }

.rev-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 150px 62px;
    align-items: center;
    gap: 0.45rem;
}

/* 머리글은 행과 열을 맞추지 않는다 — 단위 라벨(EN `per 1,000 impr.`)이 금액 칸(62px)보다
   넓어 두 줄로 접힌다. 왼쪽 제목 / 오른쪽 단위 두 칸이면 어느 폭에서도 한 줄이다 */
.rev-hd {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.45rem;
    padding-bottom: 0.22rem;
    margin-bottom: 0.16rem;
    border-bottom: 1px solid var(--m-border);
    /* 0.57rem(9.12px)은 바로 옆 0.58rem(9.28px)과 0.16px 차이라 구분이 안 되면서
       사다리 칸만 늘렸다 — 목업 최소 크기인 0.6rem 으로 흡수한다(2026-08-25) */
    font-size: 10px;
    font-weight: 700;
    color: var(--m-sub);
}

.rev-hd > span:last-child { text-align: right; }

.rev-row { padding: 0.24rem 0; font-size: 11px; }
.rev-row > b { font-weight: 600; color: var(--m-text); }

.rev-bar { height: 5px; border-radius: 3px; background: var(--m-track); overflow: hidden; }
.rev-bar i { display: block; height: 100%; width: var(--w, 0%); border-radius: 3px; background: var(--m-sub); transition: width 0.5s var(--ease-out); }

.rev-v {
    font-family: var(--font-number);
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    font-weight: 600;
    font-style: normal;
    text-align: right;
    color: var(--m-sub);
}

.rev-row.is-top > b { color: var(--m-accent); font-weight: 700; }
.rev-row.is-top .rev-bar i { background: var(--m-accent); }
.rev-row.is-top .rev-v { color: var(--m-accent); font-weight: 800; }

/* 정지 상태가 완성 상태다 — 결과 칸은 기본이 채워진 모습이고, is-live 가 붙어야 비워진다 */
.rev-out {
    display: grid;
    gap: 0.26rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--m-accent);
    border-radius: 8px;
    background: var(--m-accent-soft);
    transition: border-color 0.3s var(--ease-out), background-color 0.3s var(--ease-out);
}

.rev-out-top { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 0.5rem; }
.rev-out-top > b { font-size: 10px; font-weight: 700; color: var(--m-sub); white-space: nowrap; }
.rev-out-name { min-width: 0; font-size: 12px; font-weight: 800; color: var(--m-accent); transition: opacity 0.3s var(--ease-out); }
.rev-out-name i { display: none; font-style: normal; }
.rev-out-name i.is-on { display: inline; }

/* 예상 → 실제 → 차이. 셋이 한 줄에 서야 "이 값에서 저 값이 나왔고 그 차이가 남는다"로 읽힌다 */
.rev-kpis { display: flex; flex-wrap: wrap; gap: 0.2rem 0.5rem; }

.rev-kv {
    display: inline-flex;
    align-items: baseline;
    gap: 0.28rem;
    transition: opacity 0.3s var(--ease-out);
}

.rev-kv i { font-style: normal; font-size: 9px; font-weight: 700; color: var(--m-sub); }

.rev-kv b {
    font-family: var(--font-number);
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    font-weight: 700;
    color: var(--m-text);
}

/* 차이는 좋고 나쁨이 아니라 **되먹임의 크기**라 방향색을 쓰지 않는다 — 부호가 방향을 말한다 */
.rev-kv.is-gap { padding: 0 0.34rem; border-radius: var(--radius-pill); background: var(--m-accent); }
.rev-kv.is-gap i,
.rev-kv.is-gap b { color: var(--m-surface); }

/* 피드백 루프 — 결과에서 왼쪽으로 나가 위로 돌아 기대 수익 표에 꽂힌다.
   히어로의 큰 루프(결과 → 입력)와 다른 루프다: 여기서 도는 것은 **기대 수익 갱신**이다.
   위쪽 끝을 표 상단에 붙이면 머리글을 가리키는 것처럼 보이고 모서리와도 부딪힌다 —
   표의 세로 중앙 언저리(62px)에 꽂아 **표 전체**가 갱신 대상임을 보인다 */
.rev-loop { grid-column: 1; grid-row: 3 / span 3; position: relative; margin: 62px 0 18px; }

.rev-loop::before {
    content: '';
    position: absolute;
    left: 3px;
    right: 8px;
    top: 0;
    bottom: 0;
    border: 1px solid var(--m-border);
    border-right: 0;
    border-radius: 7px 0 0 7px;
    transition: border-color 0.3s var(--ease-out);
}

.rev-loop::after {
    content: '';
    position: absolute;
    right: 4px;
    top: -4px;
    border: 4px solid transparent;
    border-right: 0;
    border-left-color: var(--m-border);
    transition: border-left-color 0.3s var(--ease-out);
}

.rev-loop em { position: absolute; left: 1px; width: 5px; height: 5px; border-radius: 50%; background: var(--m-accent); opacity: 0; }

/* --- 움직임: 박자는 JS 가 잡고 CSS 는 전환만 한다(챕터 ① 과 같은 규약) --- */
.rev.is-live .rev-chipset i { opacity: 0; transform: translateY(3px); transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out); }
.rev.is-live.is-in .rev-chipset.is-on i { opacity: 1; transform: none; transition-delay: calc(var(--j) * 0.12s); }

.rev.is-live.is-in .rev-down.is-a em { animation: revFlow 0.7s linear; }
.rev.is-live.is-out .rev-down.is-b em { animation: revFlow 0.55s linear; }
.rev.is-live.is-calc .rev-tbl { border-color: var(--m-accent); }
.rev.is-live.is-rank .rev-down.is-b::before { border-left-color: var(--m-accent); }
.rev.is-live.is-rank .rev-down.is-b::after { border-top-color: var(--m-accent); }

.rev.is-live .rev-out { border-color: var(--m-border); background: var(--m-surface-2); }
.rev.is-live .rev-out-name,
.rev.is-live .rev-kv { opacity: 0; }
.rev.is-live.is-out .rev-out { border-color: var(--m-accent); background: var(--m-accent-soft); }
.rev.is-live.is-out .rev-out-name,
.rev.is-live.is-out .rev-kv.is-exp { opacity: 1; }
.rev.is-live.is-real .rev-kv.is-act { opacity: 1; }
.rev.is-live.is-gap .rev-kv.is-gap { opacity: 1; }

.rev.is-live .rev-loop::before { border-color: var(--m-border); }
.rev.is-live .rev-loop::after { border-left-color: var(--m-border); }
.rev.is-live.is-loop .rev-loop::before { border-color: var(--m-accent); }
.rev.is-live.is-loop .rev-loop::after { border-left-color: var(--m-accent); }
.rev.is-live.is-loop .rev-loop em { animation: revLoopFlow 0.95s linear; }

@keyframes revPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.35; transform: scale(0.72); }
}

@keyframes revFlow {
    0%   { top: 0; opacity: 0; }
    18%  { opacity: 1; }
    82%  { top: 9px; opacity: 1; }
    100% { top: 9px; opacity: 0; }
}

@keyframes revLoopFlow {
    0%   { bottom: 0; opacity: 0; }
    12%  { opacity: 1; }
    88%  { bottom: calc(100% - 5px); opacity: 1; }
    100% { bottom: calc(100% - 5px); opacity: 0; }
}

@media (min-width: 621px) and (max-width: 1080px) {
    .rev-row { grid-template-columns: minmax(0, 1fr) 104px 56px; gap: 0.34rem; }
}

/* 1열로 접히면 이름 아래에 막대와 금액이 선다 */
@media (max-width: 620px) {
    .rev { grid-template-columns: 18px minmax(0, 1fr); }
    .rev-row { grid-template-columns: minmax(0, 1fr) 62px; row-gap: 0.24rem; padding: 0.3rem 0; }
    .rev-row > b { grid-column: 1 / -1; }
    .rev-out-top { grid-template-columns: minmax(0, 1fr); }
}

/* --- 추천 엔진 계보 (기능 상세: 개인화 추천 챕터 ③) ---
   ①②③이 전부 움직이는 다이어그램이라, 마지막 챕터는 **정지된 방사도**로 둔다 —
   여기는 기계가 도는 장면이 아니라 근거를 대는 자리다.
   가운데 `10년+`(콘텐츠 추천 운영)에서 세 모델이 뻗어 나가는 그림이고, 모델 이름은
   챕터 ①의 `arch_mdl1/2/3`을 그대로 쓴다 — 그래야 "그 모델이 어디서 왔는지"가
   두 챕터를 가로질러 읽힌다.

   **종전 2열 대응표는 걷어냈다(2026-08-25 사용자).** 표가 안 읽힌 건 격자여서만이
   아니라 **챕터 ①이 이미 같은 그림**이었기 때문이다 — ①도 3행에 오른쪽 파란 배지가
   붙는 표라, 같은 배지 이름을 쓰자 ①을 한 번 더 그린 꼴이 됐다. 이름은 고리로 남기고
   배치를 방사형으로 바꿔 중복을 푼다. **여기에 표·그리드를 다시 들이지 말 것.**

   좌표계: `.orb`가 `aspect-ratio: 580/394`이고 SVG viewBox도 580×394이라
   **노드의 % 좌표가 곧 viewBox 좌표**다. 한쪽만 고치면 연결선이 노드에서 떨어진다. */
/* 카드 껍데기 없이 그림만 놓는 목업 — `.mock` 은 토큰(`--m-*`) 그릇일 뿐이라
   폭이 없다. `.chapter-visual` 이 flex 라 폭을 안 주면 아이템이 0으로 줄어든다 */
.mock-bare { width: 100%; }

.orb {
    position: relative;
    width: 100%;
    /* 620~980px 구간은 챕터가 1열이라 목업이 컨테이너 폭을 다 먹는다 —
       비율을 지키면 높이까지 같이 커져 가운데가 텅 빈다 */
    max-width: 580px;
    margin: 0 auto;
    aspect-ratio: 580 / 394;
}

.orb-web {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* `--m-border`(라이트에서 #E5E8EB) 로는 흰 면에서 거의 안 보였다(대비 1.16, 2026-08-25 사용자).
   `--m-sub` 를 옅게 섞어 ~2.0 으로 올린다 — 팔레트 회색을 직접 쓰지 않는 건 다크 목업
   (`.mock-dark`)에서도 같은 마크업이 서기 때문이다. 강조가 아니라 궤도가 있다는 정도만
   보이면 되므로 여기서 더 올리지 말 것. 잉크 비율은 dasharray 로 같이 올린다(37.5% → 43%). */
.orb-ring {
    fill: none;
    stroke: color-mix(in srgb, var(--m-sub) 40%, transparent);
    stroke-width: 1;
    stroke-dasharray: 3 4;
}

.orb-link {
    fill: none;
    stroke: var(--m-accent);
    stroke-width: 1.2;
    stroke-linecap: round;
    opacity: 0.5;
}

.orb-tip {
    fill: none;
    stroke: var(--m-accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.6;
}

/* 코어는 유일하게 채워진 원이다 — 셋이 여기서 나왔다는 걸 색 하나로 말한다 */
.orb-core,
.orb-node {
    position: absolute;
    display: grid;
    justify-items: center;
    text-align: center;
    line-height: 1.3;
}

.orb-core {
    left: 50%;
    top: 60.406%;
    width: 150px;
    /* 원의 **중심**을 기준점에 맞춘다(원 지름 88px → -44px) */
    transform: translate(-50%, -44px);
    gap: 0.3rem;
}

.orb-core-dot {
    display: grid;
    place-items: center;
    width: 88px;
    height: 88px;
    border-radius: 50%;
    background: var(--m-accent);
    /* 정지 상태가 완성 상태다 — 아래 핑은 덧입히는 모드다 */
    box-shadow: 0 0 0 8px var(--m-accent-soft);
}

/* 코어에서 은은하게 퍼져 나갔다 사라지는 핑(2026-08-25 사용자).
   이 챕터에서 유일하게 움직이는 것이고, 무엇이 도는 장면이 아니라 **살아 있다는 기척**이다.
   `box-shadow` 한 속성만 쓴다 — 헤일로를 별도 요소로 두면 코어의 파란 배경 뒤로
   보내려고 스태킹을 건드려야 하고(`.orb-core` 는 `z-index: auto` 라 컨텍스트가 없다),
   `.orb` 안의 다른 절대 배치와 순서가 엉킨다.
   ①②는 JS 가 박자를 잡지만 여기는 순수 CSS 무한 루프다 — 시퀀스가 아니라 장식이라
   IntersectionObserver·visibilitychange harness 를 붙일 이유가 없다. */
@media (prefers-reduced-motion: no-preference) {
    .orb-core-dot { animation: orbPing 3.4s linear infinite; }
}

/* **`ease-out` 을 쓰지 않는다.** 마지막 구간에서 반지름은 거의 멈추는데 알파는 아직
   남아 있어, 다 퍼진 채로 잠깐 섰다가 사라지는 것처럼 보였다(2026-08-25 사용자).
   감속은 타이밍 함수가 아니라 **키프레임 간격**으로 만든다 — 0→30% 에 12px, 30→65% 에
   10px 로 뒤가 느려진다. 그리고 **알파 0 과 확산 끝을 같은 지점(65%)에 맞춘다** —
   둘이 어긋나면 어느 쪽이든 정체로 읽힌다. 65~100% 는 보이지 않는 대기라 다음 핑까지
   숨을 준다(3.4s 중 1.2s). */
@keyframes orbPing {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--m-accent) 34%, transparent); }
    30%  { box-shadow: 0 0 0 12px color-mix(in srgb, var(--m-accent) 15%, transparent); }
    65%  { box-shadow: 0 0 0 22px color-mix(in srgb, var(--m-accent) 0%, transparent); }
    100% { box-shadow: 0 0 0 22px color-mix(in srgb, var(--m-accent) 0%, transparent); }
}

/* **`--font-number`를 쓰지 않는다.** `10년+`는 `1`·`0`·`+`가 Inter, `년`만 Pretendard로
   떨어져 네 글자에 서체가 둘이 된다(CDP `getPlatformFontsForNode` 실측: Inter×3 + Pretendard×1).
   숫자와 한글이 섞인 문자열에는 `--font-sans` 하나로 간다 — 이 크기(20px 800)에서
   두 서체의 획 굵기·x-height 차이가 눈에 띈다 */
.orb-core-dot > b {
    font-family: var(--font-sans);
    font-size: 20px;
    font-weight: 800;
    letter-spacing: -0.015em;
    color: var(--white);
}

.orb-core > i {
    font-style: normal;
    font-size: 11px;
    font-weight: 600;
    color: var(--m-sub);
}

.orb-node {
    width: 220px;
    transform: translate(-50%, -36px);
    gap: 0.34rem;
}

/* ①은 라벨이 원 위에 온다. 라벨 줄 수가 언어마다 달라(EN은 두 줄) 위에서 재면
   원이 흔들린다 — `bottom`으로 **원의 아래 모서리**를 고정하고 라벨은 위로 자라게 둔다 */
.orb-node[data-o="1"] {
    left: 50%;
    bottom: 67.259%;
    transform: translate(-50%, 0);
}
.orb-node[data-o="1"] > .orb-dot { order: 3; }

.orb-node[data-o="2"] { left: 28.345%;  top: 78.807%; }
.orb-node[data-o="3"] { left: 71.655%;  top: 78.807%; }

.orb-dot {
    display: grid;
    place-items: center;
    width: 72px;
    height: 72px;
    border: 1px solid var(--m-border);
    border-radius: 50%;
    background: var(--m-surface);
    color: var(--m-accent);
    box-shadow: var(--m-shadow-sm, 0 1px 2px rgba(16, 24, 40, 0.05));
}

.orb-dot > svg {
    width: 34px;
    height: 34px;
    fill: none;
    stroke: currentColor;
    /* viewBox 24 기준 굵기라 34px 로 키우면 실제 획이 1.4배가 된다 — 2 그대로 두면 뭉친다 */
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* 웨이트는 페이지 표준을 따른다 — 굵은 글자 700, 보조 600.
   800/500 으로 잡았다가 되돌렸다(2026-08-25 사용자: "글자 가독성이 안 좋다"):
   12.5px 한글에서 800 은 `상`·`품`·`석` 속공간이 메워지고, 10.9px 회색 500 은
   페이지에서 가장 얇은 글자가 되어 위는 뭉치고 아래는 흐려 보였다.
   챕터 ①②는 700/600 이라 이 목업만 튀었다. */
.orb-node > b {
    font-size: 12px;
    font-weight: 700;
    color: var(--m-text);
}

.orb-node > i {
    font-style: normal;
    font-size: 11px;
    font-weight: 600;
    color: var(--m-sub);
}

/* 좁은 화면에서는 방사가 성립하지 않는다 — 코어를 맨 위에 두고 세로로 편다.
   원은 그대로 남겨 "둥근 배치"의 흔적을 유지한다.
   절대 배치를 푸는 규칙은 `.orb-node`(0,1,0)에 있는데 ①의 배치는 `[data-o="1"]`(0,2,0)이라
   **`transform`·`bottom`·`order`를 여기서 같이 되돌려야 한다** — 안 그러면 ①만 왼쪽으로
   반 칸 밀려 화면 밖으로 나간다(실측) */
@media (max-width: 620px) {
    .orb {
        max-width: none;
        aspect-ratio: auto;
        display: grid;
        gap: 0.5rem;
    }

    .orb-web { display: none; }

    .orb-core,
    .orb-node {
        position: static;
        transform: none;
        width: auto;
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        align-items: center;
        justify-items: start;
        column-gap: 0.6rem;
        row-gap: 0.08rem;
        text-align: left;
    }

    .orb-node[data-o="1"] {
        bottom: auto;
        transform: none;
    }
    .orb-node[data-o="1"] > .orb-dot { order: 0; }

    .orb-core { padding-bottom: 0.55rem; border-bottom: 1px solid var(--m-border); }
    .orb-core-dot { flex: none; width: 60px; height: 60px; box-shadow: none; }
    .orb-core-dot > b { font-size: 13px; }

    /* 아이콘이 이름·설명 두 줄을 함께 물고 있어야 한 덩어리로 읽힌다 */
    .orb-dot { grid-row: span 2; width: 52px; height: 52px; }
    .orb-dot > svg { width: 25px; height: 25px; }

    .orb-node > b,
    .orb-node > i { grid-column: 2; }
}

/* --- 렌더링 제어 (기능 상세: 다양한 광고 포맷 챕터 ①) ---
   2026-08-25 개인화 추천 챕터 ③에서 통째로 옮겨 왔다(사용자). 그 페이지에는 남아 있지 않다.
   여덟 판을 버리고 왔다. 마지막까지 남아 있던 문제는 가운데 열이었다 —
   `</> 2번 칸` 같은 칩은 결정을 **글로 적은 것**이라 직관적이지 않았다
   (2026-08-25 사용자). 지금은 **움직임이 그 말을 대신한다**: 추천 리스트 3건 중
   둘이 코드 게이트를 통과해 인벤토리 슬롯에 가서 놓이고, 하나는 게이트에서 ✕로 막힌다.
   정지 상태(= `prefers-reduced-motion`, 화면 밖)가 곧 **완성 상태**다 —
   HTML 은 슬롯이 다 찬 그림을 들고 있고, `.is-live` 가 그걸 되돌린 뒤 다시 채운다.
   (`.mock-cand`는 랜딩(solution/index.html)이 계속 쓰므로 지우지 않는다) */
.rnd-wrap { width: 100%; }

.rnd {
    position: relative;
    width: 100%;
    display: grid;
    /* 가운데 기둥은 `</>` 한 글자면 되므로 좁을수록 좋다 — 여기서 아낀 폭이 곧
       상품명이 한 줄에 들어가느냐를 정한다 */
    grid-template-columns: minmax(0, 1fr) 18px 56px 18px minmax(0, 1.06fr);
    align-items: stretch;
    column-gap: 0.4rem;
}

/* 세 상자의 행이 서로 맞아야 한 흐름으로 읽힌다.
   열을 `minmax(0, 1fr)`로 못박는 이유 — 암시적 열은 max-content라, `nowrap` 캡션 하나가
   상자 폭을 무시하고 열을 늘려 안의 것들을 칸 밖으로 끌고 나간다(가운데 기둥이 그랬다) */
.rnd-box {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto repeat(3, minmax(0, 1fr));
    gap: 0.75rem;
    padding: 0.75rem;
    border: 1px solid var(--m-border);
    border-radius: 12px;
    background: var(--m-surface-2);
}

/* 가운데 상자는 게이트 하나가 세 행을 통째로 쓴다 */
.rnd-box.is-code { grid-template-rows: auto minmax(0, 1fr); }

/* 가로로 설 때 가운데 칸은 `</>` 하나가 서는 좁은 기둥이라, 좌우 상자만큼 넓힐 수 없다
   (넓히면 그만큼 상품명이 두 줄로 접힌다 — 실측으로 확인). 상자 크롬을 지워 캡션에
   칸 폭을 통째로 내주고, 남으면 두 줄로 접히게 둔다. 세로 여백은 남겨 좌우 캡션과
   같은 줄에 선다. 세로로 쌓이는 600px 아래에서는 셋이 같은 상자여야 해서 그대로 둔다 */
@media (min-width: 601px) {
    .rnd-box.is-code {
        padding-inline: 0;
        border: none;
        background: none;
    }

    .rnd-box.is-code .rnd-cap {
        justify-content: center;
        text-align: center;
        white-space: normal;
    }
}

/* 오른쪽만 진짜 화면이다 — 흰 바탕과 그림자로 앞에 세운다 */
.rnd-box.is-scr { background: var(--m-surface); box-shadow: var(--m-shadow); }

.rnd-cap {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
    padding: 0 0.06rem;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.02em;
    line-height: 1.3;
    color: var(--m-sub);
    white-space: nowrap;
}

.rnd-box.is-res .rnd-cap { color: var(--m-accent); }
.rnd-cap.is-shelf { white-space: normal; }
.rnd-cap.is-shelf > b { font-size: 12px; font-weight: 800; color: var(--m-text); }
.rnd-cap.is-shelf > i { font-style: normal; font-size: 10px; font-weight: 600; color: var(--m-sub); }

.rnd-arw {
    align-self: center;
    width: 18px;
    height: 9px;
    background: var(--m-accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 10'%3E%3Cpath d='M0 5h14M10.6 1.4 15 5l-4.4 3.6' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 10'%3E%3Cpath d='M0 5h14M10.6 1.4 15 5l-4.4 3.6' fill='none' stroke='black' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
}

.rnd-item,
.rnd-slot {
    position: relative;
    display: grid;
    align-items: center;
    gap: 0.46rem;
    padding: 0.8rem 0.5rem;
    border: 1px solid var(--m-border);
    border-radius: 9px;
    background: var(--m-surface);
    transition: border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease;
}

.rnd-item { grid-template-columns: auto 40px minmax(0, 1fr) auto; }
.rnd-slot { grid-template-columns: 40px minmax(0, 1fr) auto; }
.rnd-slot.is-ad { border-color: var(--m-accent); background: var(--m-accent-soft); }

.rnd-rk {
    font-family: var(--font-number);
    font-style: normal;
    font-size: 11px;
    font-weight: 800;
    color: var(--m-accent);
}

.rnd-item.is-off .rnd-rk { color: var(--m-sub); }

.rnd-th {
    display: grid;
    place-items: center;
    overflow: hidden;
    width: 40px;
    height: 40px;
    border-radius: 8px;
    background: var(--m-track);
}

.rnd-th img { width: 100%; height: 100%; object-fit: cover; }

.rnd-meta { display: grid; gap: 0.06rem; min-width: 0; }
/* 이름은 **한 줄에 들어가야 한다** — 두 줄이 되면 행 높이가 서로 어긋나 세 상자의 흐름이 깨진다.
   폭을 아무리 돌려도 12px 에서는 EN 이 안 들어가서 11px 로 내렸다(하한 10px 은 지킨다) */
.rnd-meta > b {
    font-size: 11px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--m-text);
    transition: color 0.3s ease;
}

.rnd-meta > i {
    font-family: var(--font-number);
    font-style: normal;
    font-size: 11px;
    color: var(--m-sub);
}

/* 못 나간 한 건은 **묻어서** 보여 준다(2026-08-25 사용자: "플랫폼 코드에서 차단한건 더 눈에
   보이게 ... 추천 리스트 자체를 회색으로 묻어버리던지"). ✕ 하나로는 12px 짜리 점이라 안 보였다.
   다만 **opacity 로 행을 통째로 죽이지는 않는다** — 글자가 대비 미달이 되고, 안 그린 상품도
   추천 리스트에는 분명히 들어 있었다. 그래서 바탕·테두리·사진만 빼고 **글자는 `--m-sub`** 로
   눌러 읽히는 상태를 유지한다 */
.rnd-item.is-off { background: var(--m-surface-2); border-style: dashed; }
.rnd-item.is-off .rnd-meta > b { color: var(--m-sub); }
.rnd-item.is-off .rnd-th img { filter: grayscale(1); opacity: 0.45; }
.rnd-th img { transition: filter 0.3s ease, opacity 0.3s ease; }

.rnd-x {
    width: 16px;
    height: 16px;
    background: var(--m-sub);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 2.6 9.4 9.4M9.4 2.6 2.6 9.4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 2.6 9.4 9.4M9.4 2.6 2.6 9.4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
    transition: opacity 0.3s ease;
}

/* `</>` 한 글자가 "여기부터 플랫폼 코드"를 대신한다 — 코드를 실제로 적으면
   개발자 아닌 독자에게 벽이 된다. 상품은 이 기둥을 통과하거나 여기서 막힌다 */
.rnd-gate {
    position: relative;
    display: grid;
    place-items: center;
    border: 1px dashed var(--m-accent);
    border-radius: 9px;
    background: var(--m-surface);
    transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}

.rnd-gate > i {
    font-family: var(--font-number);
    font-style: normal;
    font-size: 15px;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--m-accent);
    transition: opacity 0.2s ease;
}

.rnd-gate.is-hit { border-style: solid; background: var(--m-accent-soft); box-shadow: 0 0 0 3px var(--m-accent-soft); }
.rnd-gate.is-block { border-style: solid; border-color: var(--m-border); background: var(--m-surface-2); }
.rnd-gate.is-block > i { opacity: 0; }

.rnd-gate.is-block::after {
    content: '';
    position: absolute;
    width: 15px;
    height: 15px;
    background: var(--m-sub);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 2.6 9.4 9.4M9.4 2.6 2.6 9.4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 2.6 9.4 9.4M9.4 2.6 2.6 9.4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* 옮겨지는 상품 — `.rnd` 원점 기준 absolute 라 JS 가 transform 만 갱신하면 된다 */
.rnd-fly {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 3;
    overflow: hidden;
    width: 40px;
    height: 40px;
    border-radius: 9px;
    background: var(--m-surface);
    box-shadow: 0 8px 18px rgba(15, 23, 42, 0.24), 0 0 0 1px var(--m-accent);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.62s var(--ease-out), opacity 0.26s ease;
}

.rnd-fly img { width: 100%; height: 100%; object-fit: cover; }
.rnd-fly.is-on { opacity: 1; }

/* --- 재생 중에만 되돌리는 값들 (정지 상태 = 완성 상태) --- */
.rnd.is-live .rnd-slot.is-ad {
    border-style: dashed;
    border-color: var(--m-border);
    background: var(--m-surface-2);
}

.rnd.is-live .rnd-slot.is-ad > * { opacity: 0; transition: opacity 0.28s ease; }
.rnd.is-live .rnd-x { opacity: 0; }

/* 재생 중에는 **게이트에서 막히기 전까지** 멀쩡해야 한다 — 미리 회색이면 결과를 먼저 말해 버린다 */
.rnd.is-live .rnd-item.is-off {
    border-style: solid;
    border-color: var(--m-border);
    background: var(--m-surface);
}

.rnd.is-live .rnd-item.is-off .rnd-meta > b { color: var(--m-text); }
.rnd.is-live .rnd-item.is-off .rnd-th img { filter: none; opacity: 1; }

.rnd.is-live.x1 .rnd-item.is-off { border-style: dashed; background: var(--m-surface-2); }
.rnd.is-live.x1 .rnd-item.is-off .rnd-meta > b { color: var(--m-sub); }
.rnd.is-live.x1 .rnd-item.is-off .rnd-th img { filter: grayscale(1); opacity: 0.45; }

.rnd.is-live.p0 .rnd-slot[data-slot="0"],
.rnd.is-live.p2 .rnd-slot[data-slot="2"] {
    border-style: solid;
    border-color: var(--m-accent);
    background: var(--m-accent-soft);
}

.rnd.is-live.p0 .rnd-slot[data-slot="0"] > *,
.rnd.is-live.p2 .rnd-slot[data-slot="2"] > * { opacity: 1; }

.rnd.is-live.x1 .rnd-x { opacity: 1; }
.rnd.is-live .rnd-item.is-lift { border-color: var(--m-accent); box-shadow: 0 0 0 2px var(--m-accent-soft); }

@media (min-width: 521px) and (max-width: 1000px) {
    .rnd { max-width: 660px; margin-inline: auto; }
}

/* 세 열이 좁아지는 두 구간 — 이름이 두 줄로 접히기 직전이라 기둥과 여백을 더 깎는다.
   `.rnd` 실폭 기준으로 잡은 경계다: 981~1180 은 챕터가 2열이라 464~568px,
   601~720 은 챕터가 1열이지만 화면이 좁아 500~620px 다 */
@media (min-width: 601px) and (max-width: 720px),
       (min-width: 981px) and (max-width: 1180px) {
    .rnd { grid-template-columns: minmax(0, 1fr) 12px 34px 12px minmax(0, 1.03fr); column-gap: 0.26rem; }
    .rnd-arw { width: 12px; height: 8px; }
    .rnd-box { padding: 0.5rem; gap: 0.6rem; }
    .rnd-item, .rnd-slot { gap: 0.3rem; padding-inline: 0.3rem; }
    .rnd-th { width: 30px; height: 30px; }
    .rnd-item { grid-template-columns: auto 30px minmax(0, 1fr) auto; }
    .rnd-slot { grid-template-columns: 30px minmax(0, 1fr) auto; }
    /* 34px 기둥에는 EN `Platform` 한 낱말도 안 들어간다 — 화살표 고랑(양쪽 20px)에서
       9px씩 빌린다. 흘러넘치게 두는 것과 결과는 같지만 의도가 드러난다 */
    .rnd-box.is-code .rnd-cap { margin-inline: -9px; }
}

/* 1열에서는 상자 셋을 위아래로 쌓고 화살표를 세운다. 게이트도 가로 막대가 된다.
   600px 아래로는 아무리 깎아도 상품명이 한 줄에 안 들어가서 여기서 접는다 */
@media (max-width: 600px) {
    .rnd { grid-template-columns: minmax(0, 1fr); row-gap: 0.22rem; }
    .rnd-arw { justify-self: center; width: 13px; height: 24px; transform: rotate(90deg); }
    .rnd-box { gap: 0.36rem; padding: 0.55rem; }
    .rnd-box.is-code { grid-template-rows: auto auto; }
    .rnd-gate { min-height: 44px; }
    .rnd-item, .rnd-slot { padding-block: 0.36rem; }
}

/* --- 소재 조립 (기능 상세: 다양한 광고 포맷) ---
   광고주가 올린 소재가 없다는 것이 요점이라, 왼쪽은 업로드 폼이 아니라 이미 있는
   카탈로그 필드 목록이고 오른쪽은 그 필드로 조립된 지면 카드 한 장이다.
   히어로 갤러리·자리 지도와 겹치지 않는 유일한 가로 대응 구조라 이 챕터에만 쓴다. */
.asm {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 26px minmax(0, 0.58fr);
    grid-template-rows: auto minmax(0, 1fr);
    align-items: start;
    column-gap: 0.55rem;
}

/* 두 라벨은 각 상자 안이 아니라 상자 위 같은 줄에 둔다 — 왼쪽은 상자 안, 오른쪽은
   상자 밖이 되면 대응 관계가 아니라 서로 다른 층위로 읽힌다 */
.asm-head {
    grid-row: 1;
    margin-bottom: 0.45rem;
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
}

.asm-src,
.asm-arrow,
.asm-out { grid-row: 2; }

.asm-arrow { align-self: center; }

.asm-src {
    min-width: 0;
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    background: var(--m-surface-2);
}

.asm-f {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.7rem;
    padding: 0.36rem 0;
    font-size: 11px;
    color: var(--m-sub);
}

.asm-f + .asm-f { border-top: 1px solid var(--m-border); }

.asm-f b {
    min-width: 0;
    font-size: 11px;
    font-weight: 600;
    color: var(--m-text);
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.asm-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--m-accent);
}

.asm-arrow svg { width: 22px; height: 22px; }

/* 왼쪽(카탈로그 필드)은 회색 데이터 상자, 오른쪽은 흰 화면 카드 — 톤이 바뀌는
   지점이 "여기서부터 플랫폼 화면"을 말한다 */
.asm-out {
    min-width: 0;
    padding: 0.6rem;
    box-shadow: var(--shadow-2);
}

/* 카드가 좁은 열에서 혼자 늘어나면 진열 카드가 아니라 배너로 보인다 */
.asm-out .mock-card { max-width: 132px; }

/* 좁은 폭에서는 좌우 대응이 안 되므로 세로로 세우고 화살표를 돌린다 */
/* 좁은 폭에서는 세로로 세우고 화살표를 돌린다. 라벨 두 개를 위에 몰아 두면
   어느 상자의 이름인지 알 수 없으므로 각자 자기 상자 앞으로 되돌려 놓는다 */
@media (max-width: 700px) {
    .asm { display: flex; flex-direction: column; }
    .asm-head.is-gap { display: none; }
    .asm-head.is-src { order: 1; }
    .asm-src { order: 2; }
    .asm-arrow { order: 3; height: auto; transform: rotate(90deg); padding: 0.35rem 0; }
    .asm-head.is-out { order: 4; margin-top: 0.6rem; }
    .asm-out { order: 5; }
    .asm-out .mock-card { max-width: 148px; margin: 0 auto; }
}



/* --- 띠배너 (지면 상단 구좌) ---
   기존에 팔던 배너 자리도 같은 응답으로 채운다는 것을 보이는 목업이라,
   따로 만든 이미지가 아니라 **상품 컷 + 카탈로그 문구**로 조립된 모양으로 그린다. */
.mock-banner {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.55rem 0.55rem 0.45rem;
    padding: 0.55rem;
    border-radius: 10px;
    background: var(--m-accent-soft);
    box-shadow: inset 0 0 0 1px var(--m-accent);
}

.mock-banner-art {
    width: 46px;
    height: 46px;
    flex: none;
    border-radius: 8px;
    overflow: hidden;
    background: var(--m-track);
}

.mock-banner-art img { width: 100%; height: 100%; object-fit: cover; display: block; }

.mock-banner-body { flex: 1; min-width: 0; }

.mock-banner-body b {
    display: block;
    font-size: 12px;
    font-weight: 700;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-banner-body i {
    display: block;
    margin-top: 2px;
    font-style: normal;
    font-size: 10px;
    color: var(--m-sub);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mock-banner-cta {
    flex: none;
    padding: 0.3rem 0.6rem;
    border-radius: var(--radius-pill);
    background: var(--m-accent);
    color: var(--m-surface);
    font-size: 10px;
    font-weight: 700;
    white-space: nowrap;
}

/* --- 상품 상세 + 함께 본 상품 (추천 구좌) ---
   `.mock-pdp`는 폰 안에서 위로 열리는 오버레이라 절대배치다. 여기서는 지면 조각
   하나만 필요하므로 상단 바(`.mock-pdp-bar`)만 가져다 쓰고 나머지는 새로 짠다. */
.mock-detail-top {
    display: flex;
    gap: 0.6rem;
    padding: 0.6rem;
}

.mock-detail-top img {
    width: 52px;
    height: 66px;
    flex: none;
    border-radius: 7px;
    object-fit: cover;
    background: var(--m-track);
}

.mock-detail-info {
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.18rem;
}

.mock-detail-info b { font-size: 12px; font-weight: 700; color: var(--m-text); }
.mock-detail-info i { font-style: normal; font-size: 10px; color: var(--m-sub); }

.mock-detail-info em {
    font-style: normal;
    font-family: var(--font-number);
    font-size: 13px;
    font-weight: 700;
    color: var(--m-text);
}

.mock-detail-rec {
    padding: 0.5rem 0.6rem 0.6rem;
    border-top: 1px solid var(--m-border);
}

.mock-detail-rec > em {
    display: block;
    margin-bottom: 0.42rem;
    font-style: normal;
    font-size: 10px;
    font-weight: 600;
    color: var(--m-sub);
}

.mock-detail-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.35rem;
}

.mock-detail-strip > span {
    position: relative;
    aspect-ratio: 3 / 4;
    border-radius: 6px;
    overflow: hidden;
    background: var(--m-track);
}

.mock-detail-strip img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mock-detail-strip > span.is-ad { box-shadow: 0 0 0 2px var(--m-accent); }

.mock-detail-strip .mock-ad-badge {
    position: absolute;
    top: 3px;
    left: 3px;
    background: var(--m-surface);
}

/* [RT-BLOCK] */
/* --- 광고주 대시보드 챕터 목업 (기능 상세: 광고주 대시보드) ---
   히어로 대시보드(.perf-console)는 페이지 템플릿이라 feature.css 에 있고,
   여기는 챕터 안에 들어가는 화면 조각들이다 — .rule / .rnd 와 같은 기준.
   히어로 리드문이 약속하는 셋(캠페인 관리 · 성과 분석 · 광고비 확인)을 챕터가 받는다:
   ① .perf-log.is-place  지면별 성과 — 내부·메타·카카오가 한 표로
   ② .perf-prods         상품별 성과 — 광고비 대비 매출액(ROAS)을 상품 단위로
   ③ .perf-log.is-camp   캠페인 — 광고주가 직접 만들고 끄고 켠다(+ .perf-new 생성 시트)
   ①③ 이 둘 다 표지만 결이 다르다 — ① 은 **숫자 표**(노출·클릭·스파크라인·ROAS),
   ③ 은 **조작 표**(상태 토글 + 예산·기간)에 옆으로 겹친 입력 시트가 붙는다.
   ② 는 상품 이미지가 붙는 목록이다.
   .perf-chip / .perf-mchip 은 함께 쓰는 작은 부품이다. */

.perf-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    line-height: 1.6;
    padding: 2px 8px;
    border-radius: 6px;
    border: 1px solid var(--m-border);
    background: var(--m-surface);
    color: var(--m-sub);
    white-space: nowrap;
}

.perf-chip.is-on {
    border-color: var(--m-accent);
    background: var(--m-accent-soft);
    color: var(--m-accent);
    font-weight: 600;
}


/* --- 표 둘이 공유하는 규칙 -------------------------------------------
   .mock-table 은 첫 열을 42% 로 잡는데 여기는 열이 다섯이라 좁아야 하고,
   글자도 한 단계 줄인다. 열 폭은 변형별로 다시 준다. */
.perf-log th,
.perf-log td { padding: 0.3rem 0.3rem; }
.perf-log th { font-size: 9px; }
.perf-log td { font-size: 10px; }

.perf-log th:first-child,
.perf-log td:first-child { width: auto; }

/* 표 끝에 선이 남으면 잘린 것처럼 보인다 */
.perf-log tbody tr:last-child td { border-bottom: 0; }

/* --- ① 지면별 성과 — 첫 열에 플랫폼 안/밖 배지가 함께 들어간다 --- */
.perf-log.is-place th:nth-child(1),
.perf-log.is-place td:nth-child(1) { width: 34%; }
.perf-log.is-place th:nth-child(2),
.perf-log.is-place td:nth-child(2) { width: 17%; }
.perf-log.is-place th:nth-child(3),
.perf-log.is-place td:nth-child(3) { width: 15%; }
.perf-log.is-place th:nth-child(4),
.perf-log.is-place td:nth-child(4) { width: 19%; }

/* 열 넷이 다 오른쪽 정렬 숫자면 어느 줄이 잘했는지가 안 보인다. 그래서 CTR 열을 걷고
   (클릭÷노출이라 두 열로 이미 읽힌다) 그 자리에 스파크라인을 넣는다.
   막대가 아니라 추이인 이유: 막대는 옆 칸 숫자를 한 번 더 그릴 뿐이고,
   전환을 못 받는 메타 행은 빈 트랙만 남아 결함처럼 보였다(2026-08-25 사용자).
   척도는 행마다 따로다 — 검색 결과(1,842)와 피드(120)를 같은 축에 두면 넷이 바닥에 붙는다. */
.perf-spark {
    display: block;
    width: 100%;
    max-width: 60px;
    height: 18px;
    margin: 0 auto;
    overflow: visible;
}

.perf-spark polyline {
    fill: none;
    stroke: var(--m-accent);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}

.perf-log td.is-roas { font-weight: 600; }

/* 숫자 열은 오른쪽 정렬이지만 그림 열은 가운데다 — 머리말도 같이 가운데로 보낸다 */
.perf-log.is-place th:nth-child(4),
.perf-log.is-place td:nth-child(4) { text-align: center; }

/* --- ② 상품별 성과 — 표가 아니라 상품 이미지가 붙는 목록이다 --- */
.perf-prods { display: flex; flex-direction: column; }

.perf-prods > li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--m-track);
}

.perf-prods > li:last-child { border-bottom: 0; padding-bottom: 0; }

.perf-prod-img {
    flex: none;
    width: 42px;
    height: 42px;
    border-radius: 8px;
    overflow: hidden;
    background: var(--m-surface-2);
    border: 1px solid var(--m-border);
}

.perf-prod-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

.perf-prod-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.perf-prod-body > b {
    font-size: 12px;
    font-weight: 600;
    color: var(--m-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.perf-prod-body > em,
.perf-prod-num > em {
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    font-style: normal;
    color: var(--m-sub);
}

.perf-prod-num {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
}

.perf-prod-num > b {
    font-family: var(--font-number);
    font-variant-numeric: tabular-nums;
    font-size: 13px;
    font-weight: 700;
    color: var(--m-text);
}

/* 지면/매체 토글 — 실제 화면의 필터 칩 줄 */
.perf-mtabs { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 9px; }

/* 매체명은 마크로 말한다(랜딩 히어로와 같은 규칙) — 공식 CI 가 이 목업의 유일한 유채색이다.
   행 배지는 글자로 남긴다: 마크만으로는 "IG = 메타"가 안 읽힌다 */
.perf-ci { width: 20px; height: 20px; flex: none; border-radius: 5px; }

/* 메타·카카오는 대시보드 컨테이너 밖(오른쪽)에 두고 선으로만 잇는다 (2026-08-25 사용자).
   세 판을 버렸다 — ⓐ 패널 *안* 연동 상태 줄은 출처를 적기만 해서 "가져온다"가 그림이 안 됐고,
   ⓑ 위쪽 배치 + 한 방향 화살표는 "받아만 온다"로 읽혔고,
   ⓒ 점선 테두리 카드 + 점선 양방향 화살표는 **기호가 너무 많았다** — 테두리·점선·촉이 다 말을 했다.
   지금은 [실선 레일 + 양방향으로 흐르는 점] 하나가 말한다. 카드 상자도, 촉도 없다.
   내부 노드는 없다: 내부는 컨테이너 그 자체라는 게 이 그림의 요점이다. */
.perf-feed {
    display: flex;
    align-items: center;
    /* 레일이 패널 테두리에서 바로 출발해야 이어져 보인다 */
    gap: 0;
}

.perf-feed > .mock-panel { flex: 1; min-width: 0; }

.perf-feed-out {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.perf-feed-cap {
    align-self: flex-end;
    font-size: 12px;
    color: var(--m-sub);
}

.perf-feed-node { display: flex; align-items: center; gap: 7px; }

.perf-feed-link {
    position: relative;
    flex: none;
    width: 44px;
    height: 7px;
}

/* 레일은 크롬, 점이 데이터다 — 위계를 뒤집지 말 것 */
.perf-feed-link::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    border-top: 1px solid var(--m-border);
}

/* 레일마다 점 하나가 왕복한다(`alternate`) — 나갔다 들어오는 걸 점 하나로 말한다.
   레일마다 둘씩 넣고 계속 흘려 보냈더니 "과하다"는 지적을 받았다(2026-08-25 사용자).
   양 끝의 정지 구간(0~14% / 86~100%)이 이 연출의 핵심이다 — 쉬지 않고 움직이면
   로딩 스피너처럼 읽히고, 쉬어 줘야 한 번의 요청/응답으로 읽힌다. */
.perf-feed-dot {
    position: absolute;
    top: 50%;
    width: 5px;
    height: 5px;
    margin: -2.5px 0 0 -2.5px;
    border-radius: 50%;
    background: var(--m-accent);
    /* 레일 위를 지날 때 점이 선에 묻히지 않도록 면 색 링을 두른다 */
    box-shadow: 0 0 0 2px var(--m-surface);
    animation: perfFeedX 3.2s ease-in-out infinite alternate;
}

/* 둘이 같이 움직이면 기계로 보인다 */
.perf-feed-node.is-2 .perf-feed-dot { animation-delay: 1.4s; }

@keyframes perfFeedX {
    0%, 14% { left: 3%; opacity: 0; }
    26% { opacity: 1; }
    74% { opacity: 1; }
    86%, 100% { left: 97%; opacity: 0; }
}

@keyframes perfFeedY {
    0%, 14% { top: 3%; opacity: 0; }
    26% { opacity: 1; }
    74% { opacity: 1; }
    86%, 100% { top: 97%; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .perf-feed-dot { animation: none; left: 50%; opacity: 1; }
}

/* 상자를 없애고 마크 + 이름만 남긴다 — 테두리까지 있으면 선이 주인공이 아니게 된다 */
.perf-feed-card {
    flex: none;
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
}

.perf-feed-card > b { font-weight: 600; color: var(--m-text); }

/* 플랫폼은 열을 통째로 주지 않고 지면 이름 앞 배지로 둔다 — 위 탭과 같은 축이라
   열로 빼면 같은 값이 다섯 줄 반복된다. 내부만 accent 인 것은 의도다(우리 지면 vs 외부 매체) */
.perf-mchip {
    /* 배지 글자 수가 제각각이라(내부/메타/카카오, On-site/Meta/Kakao) 최소 폭을 고정한다 —
       안 그러면 지면 이름의 시작선이 행마다 어긋난다 */
    display: inline-block;
    min-width: 42px;
    text-align: center;
    margin-right: 5px;
    padding: 1px 5px;
    border-radius: 4px;
    background: var(--m-accent-soft);
    color: var(--m-accent);
    font-size: 9px;
    font-weight: 700;
    vertical-align: middle;
}

/* 플랫폼 밖은 중립으로 — 액센트는 우리 지면 몫이다 */
.perf-mchip.is-out { background: var(--m-track); color: var(--m-sub); }

/* --- ③ 캠페인 — 실제 화면 둘이다: 목록 표 + 생성 시트 -------------------
   목록을 이름/사유만 나열한 목록으로 그렸다가 **"실제로 사용자가 보는 화면은
   이렇지 않다"**(2026-08-25 사용자)는 지적으로 표로 되돌렸다 — 제품의
   캠페인 목록은 [이름 · ID · 상태 · 목적 · 일 예산 · 총 소진액 · 집행 기간 ·
   부스트 · 생성일] 표다(campaigns/columns/index.tsx). 폭이 좁아 넷만 쓴다.
   ① 도 표지만 그쪽은 **숫자 표**(노출·클릭·스파크라인·ROAS)고 여기는
   **조작 표**(토글 + 예산·기간)라 읽는 결이 다르다.
   **상태 칸은 토글 + 그 아래 사유 한 줄**이다(buildStatusDescText) —
   왜 멈췄는지를 화면이 말한다는 게 이 챕터의 요점이라 이 줄이 사라지면 안 된다. */
.perf-camp-two {
    --m-warn: #E4701D;
    display: flex;
    align-items: flex-start;
    width: 100%;
}

/* 나란히 놓고 4% 겹친다 — 겹치는 자리가 표의 마지막 열 오른쪽 여백이라
   가리는 정보가 없다(실측으로 최소 여백을 확인할 것) */
.perf-camp-list {
    width: 66%;
    flex: none;
    /* 표는 패널 폭을 꽉 채우므로 마지막 열이 시트 밑으로 들어간다 —
       겹치는 4% + 여유 2% 만큼 오른쪽 안쪽 여백을 더 준다(% 는 flex 컨테이너 기준) */
    padding-right: calc(1.1rem + 6%);
}

.perf-camp-sheet {
    position: relative;
    z-index: 1;
    width: 38%;
    flex: none;
    margin-left: -4%;
    /* 목록 머리(제목 + `+ 새 캠페인` 칩)와 구분선 아래에서 시작해야 한다 */
    margin-top: 44px;
}

/* 열이 넷이라 ① 보다 여백을 한 단계 더 줄이고, 금액·날짜도 한 단계 작다.
   **1024 에서 열 폭이 제일 좁다**(챕터가 2열이라 목업이 515px 밖에 안 된다) —
   비율이나 날짜 형식을 바꾸면 거기서 다시 잴 것. 실측 소요폭: 이름 98 ·
   금액 51 · 날짜 61px */
.perf-log.is-camp th,
.perf-log.is-camp td { padding: 0.3rem 0.22rem; }

.perf-log.is-camp td.num { font-size: 9px; }

.perf-log.is-camp th:nth-child(1),
.perf-log.is-camp td:nth-child(1) { width: 36%; }
.perf-log.is-camp th:nth-child(2),
.perf-log.is-camp td:nth-child(2) { width: 19%; text-align: center; }
.perf-log.is-camp th:nth-child(3),
.perf-log.is-camp td:nth-child(3) { width: 20%; }
.perf-log.is-camp th:nth-child(4),
.perf-log.is-camp td:nth-child(4) { width: 25%; }

.perf-log.is-camp td:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 상태 토글 — 노브는 면 색이라 트랙과 테두리 없이 갈린다.
   켜짐/꺼짐 둘이 아니라 셋이다: 켜짐 · 일시정지(주황, 노브는 여전히 오른쪽) · 꺼짐.
   주황이 이 목업의 유일한 유채색 예외인데 제품이 바로 그 자리에 orange-400 을
   쓰기 때문이고, **글자가 아니라 트랙에만** 준다(#E4701D 는 흰 면 대비 3.5:1 —
   마크로는 되지만 글자로는 못 읽는다). 사유 글자는 --m-sub 다.
   display 를 못 주면 시트의 dd 안 inline span 에서 크기가 무시된다 */
.perf-camp-sw {
    display: inline-block;
    position: relative;
    flex: none;
    width: 26px;
    height: 15px;
    border-radius: 999px;
    background: var(--m-track);
}

.perf-camp-sw::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--m-surface);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.perf-camp-sw.is-on { background: var(--m-accent); }
.perf-camp-sw.is-hold { background: var(--m-warn); }

.perf-camp-sw.is-on::after,
.perf-camp-sw.is-hold::after { left: auto; right: 2px; }

.perf-why {
    display: block;
    margin-top: 2px;
    font-size: 8px;
    font-style: normal;
    font-weight: 400;
    line-height: 1.3;
    color: var(--m-sub);
}

/* --- 생성 시트 — 라벨/값이 아니라 **입력 칸**이다 -----------------------
   라벨은 위, 컨트롤은 아래 — 좁고 세로로 긴 시트라야 옆 표와 실루엣이 갈린다.
   첫 칸만 포커스 링을 줘서 "지금 입력 중"이 읽히게 한다.
   토글 행만 좌우다(값이 글자가 아니라 마크라 라벨과 같은 줄이 자연스럽다) */
.perf-new { display: flex; flex-direction: column; gap: 8px; }

.perf-fld-w { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.perf-fld-w.is-row {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}

.perf-fld-l { font-size: 10px; color: var(--m-sub); }

.perf-fld {
    display: flex;
    align-items: center;
    gap: 5px;
    height: 24px;
    padding: 0 9px;
    border: 1px solid var(--m-border);
    border-radius: 7px;
    background: var(--m-surface);
    font-size: 11px;
    font-weight: 500;
    color: var(--m-text);
    white-space: nowrap;
    overflow: hidden;
}

.perf-fld.num { font-family: var(--font-number); font-variant-numeric: tabular-nums; }
.perf-fld > svg { width: 12px; height: 12px; flex: none; color: var(--m-sub); }

.perf-fld.is-focus {
    border-color: var(--m-accent);
    box-shadow: 0 0 0 2px var(--m-accent-soft);
}

.perf-tgs { display: flex; flex-wrap: wrap; gap: 4px; }

.perf-tgs > i {
    padding: 3px 8px;
    border-radius: 999px;
    background: var(--m-accent-soft);
    color: var(--m-accent);
    font-size: 10px;
    font-style: normal;
    font-weight: 600;
    white-space: nowrap;
}

.perf-camp-sheet .mock-panel-head { padding-bottom: 0.55rem; margin-bottom: 0.6rem; }

.perf-new-foot {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--m-border);
}

.perf-new-btn {
    padding: 5px 12px;
    border-radius: 7px;
    background: var(--m-accent);
    color: var(--m-surface);
    font-size: 10px;
    font-weight: 700;
    white-space: nowrap;
}

.perf-new-btn.is-ghost {
    background: transparent;
    border: 1px solid var(--m-border);
    color: var(--m-sub);
    font-weight: 500;
}

@media (max-width: 620px) {
    /* 나란히 두기엔 시트가 너무 좁아진다(영문 타겟팅 한 줄이 안 들어간다) */
    .perf-camp-two { flex-direction: column; gap: 12px; }
    .perf-camp-list,
    .perf-camp-sheet { width: 100%; margin: 0; }
    .perf-camp-list { padding-right: 1.1rem; }
    .perf-log.is-camp tbody tr:nth-child(2),
    .perf-log.is-camp tbody tr:nth-child(3) { display: none; }

    .perf-log th,
    .perf-log td { padding: 0.28rem 0.2rem; }
    .perf-log th { font-size: 8px; }
    .perf-log td { font-size: 9px; }

    /* 좁은 폭에서는 지면 열을 줄이고 노출 수 열을 넓힌다 — en 의 "Impressions" 가 잘린다.
       추이 열은 접는다: 다섯 열이 들어갈 폭이 아니고, 56px 짜리 선은 더 줄이면 못 읽는다 */
    .perf-log.is-place th:nth-child(4),
    .perf-log.is-place td:nth-child(4) { display: none; }
    .perf-log.is-place th:nth-child(1),
    .perf-log.is-place td:nth-child(1) { width: 34%; }
    .perf-log.is-place th:nth-child(2),
    .perf-log.is-place td:nth-child(2) { width: 19%; }
    .perf-log.is-place th:nth-child(3),
    .perf-log.is-place td:nth-child(3) { width: 17%; }
    .perf-mchip { min-width: 34px; margin-right: 4px; padding: 1px 4px; }

}

/* 배지 + 긴 영문 지면 이름("On-site Search results")이 한 줄에 안 들어가는 폭부터는
   배지를 이름 위로 올린다 — 일부 행만 두 줄이면 표가 들쭉날쭉해진다 */
/* 오른쪽 레일이 패널을 200px 대까지 밀어 넣는 폭부터는 위로 접는다 — 선 방향만 세로로 돌린다 */
@media (max-width: 520px) {
    .perf-feed { flex-direction: column-reverse; }

    .perf-feed-out {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: 0 18px;
    }

    .perf-feed-cap {
        width: 100%;
        align-self: center;
        margin-bottom: 6px;
        text-align: center;
    }

    .perf-feed-node { flex-direction: column-reverse; align-items: center; gap: 0; }

    .perf-feed-link {
        width: 7px;
        height: 46px;
        margin-bottom: 4px;
    }

    .perf-feed-link::before {
        left: 50%;
        right: auto;
        top: 0;
        bottom: 0;
        border-top: 0;
        border-left: 1px solid var(--m-border);
    }

    .perf-feed-dot {
        top: auto;
        left: 50%;
        animation-name: perfFeedY;
    }
}

@media (max-width: 440px) {
    /* ② 는 한 행에 숫자 넷(광고비 · 구매 · 구매액 · ROAS)이 들어간다 — 360px 에서
       한 줄에 다 넣으려면 영문이 50px 모자란다(실측). 글자를 더 줄이는 대신
       금액 줄을 통째로 아래로 내려 폭을 다 쓴다 — 일부 행만 접히면 행 높이가
       68/59 로 어긋나는데, 전 행을 같이 내리면 리듬이 유지된다. */
    /* ③ 표는 열이 넷이라 360 에서 날짜가 안 들어간다 — 한 단계 더 줄인다 */
    .perf-log.is-camp th,
    .perf-log.is-camp td { font-size: 9px; padding: 0.3rem 0.14rem; }
    .perf-log.is-camp th { font-size: 8px; }
    .perf-log.is-camp td.num { font-size: 8px; }

    .perf-prods > li { flex-wrap: wrap; row-gap: 6px; }
    .perf-prod-body { flex: 1 1 auto; }
    .perf-prod-num {
        flex: 1 0 100%;
        flex-direction: row;
        align-items: baseline;
        justify-content: space-between;
    }

    .perf-log.is-place td:first-child { white-space: normal; }
    .perf-log.is-place .perf-mchip {
        display: block;
        width: fit-content;
        min-width: 0;
        margin: 0 0 2px;
    }
}

/* [/RT-BLOCK] */
