/* Mulmit Ocean — approved category-canon mobile direction.
 *
 * This layer is intentionally last. It keeps the existing runtime data contract and
 * replaces only presentation. Mobile is the canonical product surface; desktop will
 * expand this grammar after the 390×844 hero gate has passed. */

:root {
  color-scheme: light;
}
html[data-theme="dark"] {
  color-scheme: dark;
}

.world {
  --bg: #f3f6fb;
  --bg-deep: #e8eff9;
  --panel: #ffffff;
  --panel-2: #f2f6fc;
  --panel-3: #e9f1fc;
  --ink: #171b24;
  --ink-2: #596273;
  --muted: #596273;
  --ink-3: #606a7b;
  --faint: #606a7b;
  --line: #e4e9f1;
  --line-strong: #cad4e2;
  --line-2: #cad4e2;
  --accent: #2468d8;
  --accent-ink: #ffffff;
  --accent-text: #1c56b4; /* the accent set as 12px text on a tinted chip: 5.6:1 where the accent itself was 4.19 */
  --accent-soft: rgba(36, 104, 216, .10);
  --chart: #2773f8;
  --chart-fill: rgba(39, 115, 248, .10);
  --up: #b4233c;
  --down: #245fc5;
  --green: #066d43;
  --red: #b4233c;
  --amber: #865000;
  --cal-sun: #b4233c;
  --cal-sat: #245fc5;
  --violet: #6d43c0;
  --seg-ghost: #dce7f5;
  --seg-ghost-2: #eaf0f8;
  --seg-live: #2773f8;
  --shadow: 0 14px 40px rgba(25, 72, 133, .08);
  /* Surfaces of water between the canvas and the panels, one token each so the
   * night palette can restate them without touching a rule. */
  --water-1: #f7faff;
  --water-2: #f2f7ff;
  --water-3: #edf5ff;
  --water-4: #e8f0fc;
  --water-line: #d9e6f7;
  /* 사진 위에 앉는 것들 — 막과 그 위의 잉크는 테마를 타지 않는다.
     밝은 화면에서 재생 배지가 검은 글자가 됐던 자리(운영자 2026-09-16). */
  --scrim: rgba(10, 17, 28, .62);
  --on-scrim: #ffffff;
  /* Sticky chrome is opaque: at 96% the content below ghosted through the
   * masthead and the bottom rail without a blur to excuse it. */
  --chrome: #ffffff;
  --chrome-2: rgba(255, 255, 255, .72);
  --current: #4ea8ff;
  --current-2: #68c9ff;
  --shadow-ink: 31, 73, 128;
  /* Grade letters A-E (industry percentile). The pages used to carry their own
   * dark-world tints inline; these read at 4.5:1 or better on the daylight canvas. */
  --grade-a: #066d43;
  --grade-b: #2468d8;
  --grade-c: #6d43c0;
  --grade-d: #865000;
  --grade-e: #b4233c;
  /* One elevation for every surface: an offset, soft shadow on the water
   * canvas. No drawn borders (operator, 2026-09-16: "사각형에 숫자가 갇혀
   * 있는 느낌"). */
  --surface-shadow: 0 1px 2px rgba(var(--shadow-ink), .04), 0 10px 28px rgba(var(--shadow-ink), .06);
  --surface-shadow-lift: 0 2px 4px rgba(var(--shadow-ink), .05), 0 16px 36px rgba(var(--shadow-ink), .09);
  background: var(--bg);
  color: var(--ink);
}

body.world { background: var(--bg); }
.world .masthead,
.world .jump-nav,
.world .section-rail { background: var(--chrome); }
.world .tape { background: var(--panel); }

/* Shared ocean-finance grammar. Data stays dense, but every region reads as one
 * answer with a clear edge, not as a pile of unrelated dark dashboard boxes. */
/* The canvas is a named token, defined on <html> itself. It cannot read var(--bg):
 * on <html> that still resolves to monitor.css's dark :root (re-measured 2026-09-17:
 * #0a0c0f), so the light app sat on a near-black canvas at first paint and on
 * overscroll. Declaring --canvas here keeps the value in a definition rather than
 * loose in a rule, and the overscroll colour and the page ground stay one thing. */
html:has(body.world) { --canvas: #f3f6fb; background: var(--canvas); }
.world ::selection { background: rgba(39, 115, 248, .18); color: var(--ink); }
.world :is(a, button, input, select, summary, [tabindex]):focus-visible {
  outline: 3px solid rgba(39, 115, 248, .42);
  outline-offset: 2px;
}
.world .shell { width: min(1180px, calc(100% - 40px)); }
.world .masthead {
  border-bottom: 1px solid var(--line);
  box-shadow: 0 6px 22px rgba(var(--shadow-ink), .035);
}
.world .brand-mark { width: 52px; height: 26px; }
.theme-toggle .theme-ico { display: block; width: 20px; height: 20px; }
html[data-theme="dark"] .theme-toggle .theme-ico-moon { display: none; }
html:not([data-theme="dark"]) .theme-toggle .theme-ico-sun { display: none; }
.world .brand strong { color: var(--ink); letter-spacing: -.035em; }
.world .site-search-field,
.world .icon-button,
.world .segmented,
.world .nav-chip {
  border-color: var(--line);
  background: var(--panel);
  color: var(--ink-2);
}
.world .tape {
  color: var(--ink-2);
  border-bottom-color: var(--line);
}
.world .console-section { margin-top: 52px; }
.world .console-section:first-child { margin-top: 28px; }
.world .console-section > header {
  align-items: center;
  gap: 10px 14px;
  margin-bottom: 14px;
}
.world .console-section > header :is(h1, h2) {
  color: var(--ink);
  font-size: clamp(21px, 2.2vw, 28px);
  font-weight: 760;
  letter-spacing: -.035em;
}
.world .console-section > header .sec-note,
.world .section-copy { color: var(--ink-2); }

.world :is(.panel, .metric-card, .kro-card, .cliq-card, .news-panel, .stock-section,
  .asset-section, .coin-section, .methodology) {
  border-color: transparent;
  background: var(--panel);
  box-shadow: var(--surface-shadow);
}
.world .tile {
  border-color: transparent;
  box-shadow: var(--surface-shadow);
}
.world a.tile:hover {
  border-color: transparent;
  background: var(--panel);
  box-shadow: var(--surface-shadow-lift);
  transform: translateY(-2px);
}
/* The three home panels were one box with 1px gaps showing the line colour
 * through; they are three surfaces on the water now. */
.world .home-lede {
  gap: 16px;
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.world .home-lede > :is(.pulse-strip, .pulse-side),
.world :is(.pulse-strip, .pulse-side) {
  border-color: transparent;
  border-radius: 18px;
  background: var(--panel);
  box-shadow: var(--surface-shadow);
}
/* The two private-market tiles live inside a panel: no card inside a card,
 * a hairline between them instead. */
.world .hp-tiles .tile { border-radius: 0; background: transparent; box-shadow: none; }
.world .hp-tiles .tile + .tile { border-top: 1px solid var(--line); }
.world .hp-tiles a.tile:hover { transform: none; box-shadow: none; background: var(--water-1); }
.world .disclose {
  border-color: transparent;
  background: transparent;
}
.world .ask-option {
  border-color: transparent;
  border-radius: 12px;
  background: var(--water-2);
}
.world .ask-option:hover:not(:disabled) { border-color: transparent; background: var(--water-3); }
.world .ask-option[aria-pressed="true"] { border-color: var(--accent); }
.world :is(.panel, .stock-section, .asset-section, .coin-section, .methodology) {
  border-radius: 18px;
}
.world :is(.metric-card, .kro-card, .cliq-card, .zone-link-card) {
  border-radius: 15px;
}
.world :is(.metric-card, .kro-card, .cliq-card):hover {
  border-color: transparent;
  background: var(--panel);
  box-shadow: var(--surface-shadow-lift);
}
.world :is(.metric-primary, .quote-num, .quote-live-num, .asset-price, .coin-price,
  .stock-kpi dd, .asset-kpis dd, .coin-kpis dd, .num) {
  font-variant-numeric: tabular-nums;
  letter-spacing: -.025em;
}
.world :is(.up, .positive, [data-tone="up"]) { color: var(--up); }
.world :is(.down, .negative, [data-tone="down"]) { color: var(--down); }
/* Direction colour belongs to the change line (.kro-vs, .tile-change), never to a
 * whole card: a .kro-card.up painted its name and price red (operator, 2026-09-16
 * "왜 글자까지 빨간색이야"). The name and the price read in ink; only the delta signals. */
.world .kro-card:is(.up, .down),
.world .kro-card:is(.up, .down) .kro-price,
.world .kro-card:is(.up, .down) h3 { color: var(--ink); }
.world .panel-footer,
.world .attribution,
.world .kro-footer,
.world .stock-note,
.world .asset-note,
.world .coin-note { color: var(--ink-3); }
.world .cal-kind-tag.is-release { color: var(--accent-text); }
/* The as-of fill follows the caption title as a bare text node; the EN guard
 * rewrites text nodes and can drop the space, so the gap is the span's margin. */
.world .weekend-caption > :is(.lang-ko, .lang-en) { margin-right: .3em; }
/* Warning badges are 12px on a tinted chip; --ink-3 there was 4.49:1. */
.world .status-badge:is(.warn, .stale) { color: var(--ink-2); }
/* The same grade letter reads the same colour on /kr, /analytics and the stock
 * page. /kr's board had no grade class at all (operator, 2026-09-16). */
.world #kr-top-grades .krtg-letter.grade-a { color: var(--grade-a); border-color: color-mix(in srgb, var(--grade-a) 45%, var(--line)); background: color-mix(in srgb, var(--grade-a) 10%, transparent); }
.world #kr-top-grades .krtg-letter.grade-b { color: var(--grade-b); border-color: color-mix(in srgb, var(--grade-b) 45%, var(--line)); background: color-mix(in srgb, var(--grade-b) 10%, transparent); }
.world #kr-top-grades .krtg-letter.grade-c { color: var(--grade-c); border-color: color-mix(in srgb, var(--grade-c) 45%, var(--line)); background: color-mix(in srgb, var(--grade-c) 10%, transparent); }
.world #kr-top-grades .krtg-letter.grade-d { color: var(--grade-d); border-color: color-mix(in srgb, var(--grade-d) 45%, var(--line)); background: color-mix(in srgb, var(--grade-d) 10%, transparent); }
.world #kr-top-grades .krtg-letter.grade-e { color: var(--grade-e); border-color: color-mix(in srgb, var(--grade-e) 45%, var(--line)); background: color-mix(in srgb, var(--grade-e) 10%, transparent); }
/* A play badge sits on a photograph, not on the canvas: dark glass and a white
 * glyph in both themes. Inheriting the page ink made it black in daylight
 * (operator, 2026-09-16). */
.world .news-videos .nvid-shot:not(.no-shot) .nvid-badge { background: var(--scrim); color: var(--on-scrim); }
.world .league-table caption:not(.weekend-caption) { display: none; }
.world .panel > .table-scroll,
.world .stock-section > .table-scroll,
.world .asset-section > #chart-wrap {
  border-radius: 17px;
}
.world .panel > .state-block:first-child { border-radius: 17px; }
.world .league-roster-name a {
  display: flex;
  min-height: 24px;
  align-items: center;
}

.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table) { color: var(--ink-2); }
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table) thead th {
  background: var(--water-1);
  color: var(--ink-3);
  border-bottom-color: var(--line);
}
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table) :is(th, td) { border-bottom-color: var(--line); }
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table) tbody tr:hover { background: var(--water-1); }
.world .table-scroll { scrollbar-color: var(--line-strong) transparent; }

.world :is(.state-block, .state-line, .chart-state, .scr-empty) {
  border-color: var(--line);
  border-radius: 14px;
  background: var(--water-1);
  color: var(--ink-2);
}
.world .gauge-lit,
.world .ev-by-mark::before,
.world .connection-badge i,
.world .session-badge.open i { box-shadow: none; }
.world :is(.kicker, .eyebrow) { letter-spacing: .04em; }
.world .letter-mark[data-len="2"] > span,
.world .stock-head .letter-mark.hero-logo[data-len="2"] > span { letter-spacing: -.04em; }
.world :is(.disclose, .league-fold, .score-bench-method) > summary {
  color: var(--ink-2);
  border-color: var(--line);
}
.world :is(.disclose, .league-fold, .score-bench-method)[open] > summary { color: var(--accent); }

/* Detail routes keep the current value and its basis together. */
.world :is(.stock-head, .asset-head, .coin-head) {
  border-bottom-color: var(--line);
}
.world :is(.quote, .asset-readout, .coin-readout) {
  border: 1px solid var(--water-line);
  border-radius: 20px;
  background:
    radial-gradient(90% 120% at 100% 0%, rgba(39, 115, 248, .13), transparent 56%),
    linear-gradient(180deg, var(--water-1) 0%, var(--water-3) 100%);
  box-shadow: 0 12px 34px rgba(var(--shadow-ink), .07);
}
.world .sec-nav {
  border-color: var(--line);
  background: rgba(247, 249, 252, .92);
}
.world .stock-tab { color: var(--ink-3); }
.world .stock-tab.active { color: var(--accent); }
.world .quote:has(.quote-live:not([hidden])) .quote-official {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(39, 115, 248, .12);
}
.world .quote:has(.quote-live:not([hidden])) .quote-num { font-size: 30px; }
.world .quote:has(.quote-live:not([hidden])) .quote-official-label { color: var(--ink-3); }

/* Record and reading routes open with the heading itself. The short blue dash
 * that stood above it was a kicker in disguise and exists nowhere in the comp. */
.world .news-list > * {
  border-color: var(--line);
  background: var(--panel);
}
/* Record and league surfaces float like every other panel: no drawn edge, the one
 * surface shadow (the news panel and the /score answer still carried a 1px line). */
.world .news-panel,
.world .markets-summary,
.world .weekend-answer,
.world .flow-step,
.world .league-state,
.world .league-slip,
.world .league-rule {
  border-color: transparent;
  background: var(--panel);
  box-shadow: var(--surface-shadow);
}

/* The league is a product surface too: same calm canvas, with one unmistakable
 * competition-blue introduction instead of a separate dark microsite. */
.world .league-page { color: var(--ink); }
.world .league-page .console-section:first-child {
  padding: 26px;
  border: 1px solid var(--water-line);
  border-radius: 22px;
  background:
    radial-gradient(80% 130% at 100% 0%, rgba(39, 115, 248, .15), transparent 58%),
    linear-gradient(145deg, var(--water-1) 0%, var(--water-3) 100%);
}
.world .league-page .league-method-panel,
.world .league-page .league-contract,
.world .league-page .league-contract-route > div { background: transparent; }
.world .league-page .league-jump a { color: var(--accent); border-color: var(--water-line); }
.world .league-page .league-contract-route { border-color: var(--water-line); }
.world .league-page :is(.league-hosted, .league-pending, .league-note) { color: var(--ink-2); }

/* Search is an action, so its first field is deliberately generous and obvious. */
.world .lookup,
.world .lookup-field { border-color: var(--line); background: var(--water-2); }
.world .lookup-input { color: var(--ink); }
.world .lookup-input::placeholder { color: var(--ink-3); }
.world :is(.scr-tab, .segmented button, .news-filter button, .bio-filter button) {
  min-height: 44px;
  border-radius: 12px;
}
.world :is(.scr-tab, .segmented button, .news-filter button, .bio-filter button).active,
.world :is(.scr-tab, .segmented button, .news-filter button, .bio-filter button)[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}
.world .news-filter button.active b { color: var(--accent-ink); }
/* Section switches are underline tabs, not raised chips: the chip field sat a
 * few pixels off the panel edge and the white selected chip read as a button
 * popping out of the header (operator, 2026-09-16 "align도 안 맞고 튀어나온
 * 느낌"). A text row with a 2px ocean-blue underline aligns with the heading,
 * shares the masthead's grammar, and never competes with the hero's range chips. */
.world .segmented {
  gap: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: inset 0 -1px 0 var(--line);
}
.world .segmented button {
  flex: 0 0 auto; /* the tab is as wide as its label; a stretched tab put the indicator under empty space */
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink-3);
  font-weight: 650;
  box-shadow: none;
}
.world .segmented button:first-child { padding-left: 2px; }
.world .segmented button:hover { color: var(--ink); }
.world .segmented button.active,
.world .segmented button[aria-pressed="true"] {
  color: var(--accent);
  background: transparent;
  box-shadow: none;
}
/* The 2px indicator sits under the label box, not under the button's padding. */
.world .segmented button::after { left: 12px; right: 12px; bottom: 0; background: transparent; }
.world .segmented button:first-child::after { left: 2px; }
.world .segmented button.active::after,
.world .segmented button[aria-pressed="true"]::after { background: var(--accent); }

.world .site-footer {
  color: var(--ink-3);
  border-top-color: var(--line);
}
.world .site-footer a { color: var(--ink-2); }

.ocean-range,
.ocean-quick { display: none; }

/* Lower-page navigation uses the same compact destination rhythm as the app
 * shell. It reads as one route index instead of eight interchangeable cards. */
.page-landing .zone-links {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  padding-inline: 18px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--panel);
}
.page-landing .zone-link-card {
  position: relative;
  min-height: 76px;
  padding: 16px 38px 16px 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.page-landing .zone-link-card:last-child { border-bottom: 0; }
.page-landing .zone-link-card:hover {
  border-color: var(--line);
  background: var(--water-1);
  box-shadow: none;
}
.page-landing .zone-link-card strong { color: var(--ink); font-size: 15px; }
.page-landing .zone-link-card strong::after {
  content: "›";
  position: absolute;
  top: 50%;
  right: 2px;
  color: var(--accent);
  font-size: 24px;
  font-weight: 500;
  transform: translateY(-50%);
}
.page-landing .zone-link-card > span { margin-top: 4px; }

/* The left rule is operational: its tone follows the section's live-data state.
 * It carries the current/depth language past the hero without adding scenery. */
.page-landing .console-section[data-rank] > header {
  position: relative;
  /* 글은 판 안 글 열에 서고, 룰은 판 가장자리에 남는다. 13px이던 때는 제목이
     판 안 글보다 3px 왼쪽이었다(실측 2026-09-17: 제목 159 · 판 글 162). */
  padding-left: var(--panel-inset);
}
.page-landing .console-section[data-rank] {
  position: relative;
  isolation: isolate;
  --ocean-depth-line: #c9d8ec;
  --ocean-depth-wash: rgba(36, 104, 216, .026);
}
.page-landing .console-section[data-rank="lead"] {
  --ocean-depth-line: #8bb6ee;
  --ocean-depth-wash: rgba(36, 104, 216, .045);
}
/* No wash behind a section. The tinted rounded box that used to sit behind each
 * ranked section read as one more box — two of them touching looked merged and
 * the heading sat glued to its edge (operator, 2026-09-16: "백그라운드에 박스가
 * 없어도 되지 않나"). Depth is the state rule beside the heading; the panels are
 * the only surfaces. */
.page-landing .console-section[data-rank]::after { content: none; }
/* Depth is the wash behind the section and the state bar beside its heading.
 * The panel itself carries no coloured left stripe: that is the side-tab
 * default of every dashboard, and the state already stands next to the title. */
.page-landing .console-section[data-rank] > :is(.panel, .zone-links) {
  box-shadow: var(--surface-shadow);
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="ready"]) {
  --ocean-depth-line: var(--chart);
  --ocean-depth-wash: rgba(37, 142, 247, .05);
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="waiting"], .state-block[data-state="loading"]) {
  --ocean-depth-line: var(--amber);
  --ocean-depth-wash: rgba(184, 107, 0, .035);
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="error"]) {
  --ocean-depth-line: var(--red);
  --ocean-depth-wash: rgba(180, 35, 60, .035);
}
/* 룰과 글 사이가 3px이었다 — 룰이 6px에서 서고 글이 12px(폰 `--panel-inset`)
   에서 시작해서, 막대가 제목에 붙어 읽혔다(운영자 2026-09-17 "너무 제목들이랑
   붙어있어"). 다른 페이지는 룰이 0에서 서서 9px이 남는다. 홈도 같은 자리로
   옮긴다 — 두 문법이 하나가 되고 간격이 세 배가 된다. */
.page-landing .console-section[data-rank] > header > :is(h1, h2) { position: relative; }
.page-landing .console-section[data-rank] > header > :is(h1, h2)::before {
  content: "";
  position: absolute;
  top: .16em;
  bottom: .16em;
  left: calc(var(--panel-inset) * -1);
  width: 3px;
  border-radius: 999px;
  background: var(--line-strong);
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="ready"]) > header > :is(h1, h2)::before {
  background: linear-gradient(180deg, var(--chart), var(--current-2));
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="waiting"], .state-block[data-state="loading"]) > header > :is(h1, h2)::before {
  background: var(--amber);
}
.page-landing .console-section[data-rank]:has(.state-block[data-state="error"]) > header > :is(h1, h2)::before {
  background: var(--red);
}
.page-landing #zones .zone-links {
  position: relative;
  padding-top: 4px;
}
.page-landing #zones .zone-links::after {
  content: "";
  position: absolute;
  top: 0;
  right: 18px;
  left: 18px;
  height: 2px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--current-2) 42%, transparent 86%);
}

.page-landing .home-values { position: relative; isolation: isolate; }
.page-landing .seg-fresh { white-space: nowrap; }
/* The empty hero states its reason. Hidden while the first round is still
 * loading (html.booting) and whenever the hero itself is showing. */
.page-landing .seg-hero-missing {
  display: none;
  order: 2;
  margin: 6px 0 0;
  max-width: 34ch;
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.55;
}
html:not(.booting) .page-landing .seg-readout:has(#seg-hero[hidden]) .seg-hero-missing { display: block; }
/* No dead controls under a missing value: the range buttons wait with the chart. */
html:not(.booting) .page-landing .seg-readout:has(#seg-hero[hidden]) .ocean-range { display: none; }
/* The value is a 24-hour reference; the line is a chosen span. Say which. */
.page-landing .ocean-hero-chart::after {
  content: attr(data-span);
  position: absolute;
  right: 6px;
  top: -16px;
  font: 600 11px/1 var(--sans);
  letter-spacing: 0;
  color: var(--ink-3);
  white-space: nowrap;
}
.page-landing .ocean-hero-chart:not([data-span])::after { content: none; }
/* The heatmap arrives when scrolled into view. Until then its reserved height
 * shows the shape of what is coming - blank cells, no values - beside the
 * sentence that says why it is blank. */
.tv-panel .tradingview-host,
.tv-panel .state-block:is([data-state="deferred"], [data-state="loading"]) {
  position: relative;
  background-color: var(--water-1);
  background-image:
    linear-gradient(90deg, var(--water-4) 0 37%, transparent 37% 39%, var(--water-4) 39% 68%, transparent 68% 70%, var(--water-4) 70% 100%),
    linear-gradient(90deg, var(--water-4) 0 22%, transparent 22% 24%, var(--water-4) 24% 60%, transparent 60% 62%, var(--water-4) 62% 100%);
  background-repeat: no-repeat;
  background-size: 100% 47%, 100% 51%;
  background-position: 0 0, 0 100%;
}
/* The arrival. One authored moment per new snapshot: the summary line draws
 * itself in, then each row's 30-day line flows in from the left one after another
 * (--arrive-delay from console.js), its last point pulses once and the value
 * settles from a blur. Data moving, not scenery; the final state is the static
 * page, and reduced motion gets that state at once. */
/* Every sparkline carries a tip point for the arrival; it is invisible everywhere
 * until the home board animates it (it showed as a dot on /kr cards). */
.world :is(.tile-spark, .spark-svg, .ocean-hero-chart) .tip {
  fill: currentColor;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}
.page-landing .seg-readout.is-arriving .ocean-hero-chart .line,
.page-landing .tile.is-arriving .tile-spark path.line {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: arrive-draw 760ms cubic-bezier(.2, .75, .25, 1) var(--arrive-delay, 0ms) forwards;
}
.page-landing .seg-readout.is-arriving .ocean-hero-chart .area,
.page-landing .tile.is-arriving .tile-spark path.fill {
  animation: arrive-fill 520ms ease-out calc(var(--arrive-delay, 0ms) + 380ms) both;
}
.page-landing .seg-readout.is-arriving .ocean-hero-chart .tip,
.page-landing .tile.is-arriving .tile-spark .tip {
  animation: arrive-tip 680ms cubic-bezier(.2, .75, .25, 1) calc(var(--arrive-delay, 0ms) + 700ms) both;
}
/* A tile whose digits are rolling (the odometer, .is-odo) does not also settle
 * from a blur — one motion per value. */
.page-landing .seg-readout.is-arriving .seg-hero-level,
.page-landing .tile.is-arriving .tile-value:not(:has(.is-odo)) {
  animation: arrive-settle 560ms cubic-bezier(.2, .75, .25, 1) var(--arrive-delay, 0ms) both;
}
@keyframes arrive-draw { to { stroke-dashoffset: 0; } }
@keyframes arrive-fill { from { opacity: 0; } }
@keyframes arrive-tip {
  0% { opacity: .95; transform: scale(.5); }
  55% { opacity: .4; transform: scale(2.6); }
  100% { opacity: 0; transform: scale(3.4); }
}
@keyframes arrive-settle {
  from { opacity: .3; filter: blur(6px); transform: translateY(6px); }
  to { opacity: 1; filter: blur(0); transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .page-landing .is-arriving,
  .page-landing .is-arriving * { animation: none !important; }
}

@media (min-width: 761px) {
  .page-landing .seg-readout {
    position: relative;
    /* Shorter card, less air above it (operator, 2026-09-16: "상단 여백과 코스피 카드
     * 높이를 조금 줄이기"); the chart keeps its width. */
    min-height: 0;
    margin-top: 16px;
    padding: 24px 32px 22px;
    overflow: hidden;
    border: 0;
    border-radius: 24px;
    background:
      radial-gradient(70% 150% at 100% 0%, rgba(39,115,248,.15), transparent 60%),
      linear-gradient(145deg, var(--water-1) 0%, var(--water-3) 100%);
    box-shadow: 0 16px 42px rgba(var(--shadow-ink), .065);
  }
  .page-landing .seg-status {
    position: static;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 12px;
  }
  .page-landing .seg-hero-level { order: -1; }
  .page-landing .ocean-hero-chart::after { top: auto; bottom: -16px; }
  .page-landing .seg-status .session-badge {
    width: auto;
    min-width: max-content;
    max-width: none;
    padding-inline: 12px;
    white-space: nowrap;
  }
  .page-landing .seg-hero {
    /* static: the chart's box is the card, not the anchor's 754px column, so the
     * line can run to the card's right edge (operator, 2026-09-16 "오른쪽이 너무 비는
     * 느낌"). */
    position: static;
    display: flex;
    min-height: 0;
    flex-direction: column;
    align-items: flex-start;
  }
  .page-landing .seg-hero-name { font-size: 26px; font-weight: 760; letter-spacing: -.035em; }
  .page-landing .seg-hero-line { display: flex; align-items: baseline; gap: 18px; margin-top: 12px; }
  /* The level is ink, not blue: blue means down on every other card (operator, 2026-09-16). */
  .page-landing .seg-hero-level { font-size: 58px; line-height: 1; font-weight: 760; letter-spacing: -.04em; color: var(--ink); }
  .page-landing .seg-hero-value { font-size: 28px; font-weight: 720; }
  .page-landing .seg-hero-label { margin-top: 10px; color: var(--ink-2); }
  .page-landing .ocean-hero-chart {
    position: absolute;
    top: 84px;
    /* From just past the delta (the widest level+delta ends ~402px into the card;
     * 430px keeps a gap) to the card's right padding. */
    left: max(38%, 430px);
    right: 32px;
    display: block;
    width: auto;
    height: 100px;
    color: var(--accent);
    opacity: .82;
  }
  .page-landing .ocean-hero-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
  .page-landing .ocean-hero-chart .area { fill: currentColor; opacity: .14; }
  .page-landing .ocean-hero-chart .line { fill: none; stroke: currentColor; stroke-width: 2; vector-effect: non-scaling-stroke; }
  .page-landing .ocean-hero-chart.up { color: var(--up); }
  .page-landing .ocean-hero-chart.down { color: var(--down); }
  /* The range chips sit in flow under the label. Pinned to the card's bottom they
   * rode up into the label when the card got shorter (operator, 2026-09-16 "깨졌다"). */
  .page-landing .ocean-range {
    position: static;
    z-index: 4;
    display: flex;
    gap: 4px;
    margin: 10px 0 0;
  }
  .page-landing .ocean-range button {
    min-width: 46px;
    min-height: 34px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--ink-3);
    font: 650 12px/1 var(--sans);
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
  }
  /* A chip answers the pointer (operator, 2026-09-16: "호버해도 호버 모션이 안 떠,
   * 마우스도 안 변하고"): the hand cursor, ink text and a faint panel tint. */
  .page-landing .ocean-range button:hover:not(:disabled):not([aria-pressed="true"]) {
    background: rgba(var(--shadow-ink), .07);
    color: var(--ink);
  }
  .page-landing .ocean-range button:disabled { opacity: .32; cursor: default; }
  .page-landing .ocean-range button[aria-pressed="true"] {
    background: var(--panel);
    color: var(--accent);
    box-shadow: 0 2px 10px rgba(var(--shadow-ink),.10);
  }
  .page-landing .market-heading { margin-top: 36px; }
}

@media (max-width: 760px) {
  .world .league-roster-name a {
    min-height: 44px;
  }
  :root {
    /* 96 → 64. 안에 선 것은 브랜드 44px 하나뿐이고 위아래 여백이 8px씩이라
       60px이면 꽉 찬다 — 나머지 36px은 이유 없는 빈 띠였다(운영자 제보
       2026-09-17 "상단바가 아래 공백이 너무 많이 생기는데?"). */
    --mast-h: 64px;
    /* 둥근 바의 문법은 지키되 읽는 화면을 덜 덮는다. 21px 아이콘과 12px 이름은
       58px 안에서 44px 조작 면적을 유지하고, 8px 들여쓰기가 바를 띄운다. */
    --pagebar-h: 58px;
    --pagebar-inset: 8px;
  }

  body.world {
    min-width: 0;
    /* 바가 떠 있으므로 아래 여백은 바 높이 + 들여쓰기 두 배다 — 붙어 있을
       때의 계산을 그대로 두면 마지막 줄이 바 밑으로 들어간다. */
    padding-bottom: calc(var(--pagebar-h) + var(--pagebar-inset) * 2 + env(safe-area-inset-bottom, 0px));
  }

  .world .masthead {
    position: sticky;
    top: 0;
    min-height: var(--mast-h);
    padding: max(8px, env(safe-area-inset-top, 0px)) 16px 8px;
    flex-wrap: nowrap;
    gap: 10px;
    border-bottom: 1px solid var(--line);
    box-shadow: none;
  }

  .world .brand { gap: 8px; }
  /* 10px 내리던 줄을 걷었다. 마스트헤드는 `align-items: center`로 이미 가운데를
     잡는데 그 위에 밀어 내리니 96px 바 안에서 위 36 · 아래 16으로 아래쪽에
     쏠렸다(실측 2026-09-17, 390·375px). 이유가 적혀 있지 않은 줄이었고
     (Ocean Canon 첫 커밋), 노치는 `padding-top: max(8px, env(...))`가 이미 맡는다. */
  .world .brand-mark { width: 44px; height: 22px; }
  .world .brand strong { font-size: 20px; letter-spacing: -.035em; }
  .world .mast-actions { order: 2; gap: 8px; }
  .world .site-search {
    --search-rest: 44px;
    /* **펼친 칸의 자리는 세지 않는다 — 레이아웃이 잡는다** (운영자 2026-09-20:
       "아직도 살짝 가리는데?").

       이 자리를 두 번 손으로 셌고 두 번 다 낡았다. 처음엔 왼끝을 마크 앞까지
       뻗어 이름 `Mulmit`을 덮었고(그래서 이름을 `opacity: 0`으로 숨기고
       있었다), 다음엔 `100vw - 145px`으로 이름 뒤에 세웠는데 — **같은 날
       배치에서 마크가 34×34 → 44×22로 바뀌면서**(`feat/pwa-icon-refresh`)
       간격이 10.3px에서 **0.3px**이 됐다(실측 360·390·412 전부 같은 값).

       숫자를 지우고 플렉스에 맡긴다. 열리면 `.mast-actions`가 남은 자리를
       전부 갖고 그 안에서 레인이 전부를 갖는다 — 왼끝은 브랜드가 끝나는 자리
       + 마스트헤드 간격이고, 오른끝은 셸 여백이다. 마크가 또 바뀌어도 따라
       움직인다. 단추는 `opacity`가 아니라 **자리째** 비켜야 레인이 그 자리를
       쓴다. */
    position: relative;
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
  }
  .world .masthead:has(.site-search:focus-within) .mast-actions {
    flex: 1 1 auto;
    min-width: 0;
  }
  .world .masthead:has(.site-search:focus-within) .site-search {
    flex: 1 1 auto;
    width: auto;
  }

  /* **자동완성 목록은 44px 아이콘이 아니라 화면에 맞춘다** (운영자 제보
     2026-09-20: "모바일에서 종목검색 깨져" — 목록이 세로 60px 상자가 되어
     "미국 종목"이 두 줄로 접히고 이름 칸이 0px이었다).

     이 자리는 2026-08-27에 한 번 고쳤다 — `monitor.css`가 `.site-search`의
     position을 풀어 목록의 기준을 마스트헤드로 옮겼고, 좌우를 12px씩 잡았다.
     그 고침이 **죽어 있었다**: 위 규칙(`.world .site-search`)이 레인을 44px
     짜리 positioned 상자로 되돌리는데, 선택자가 한 칸 더 길어(0,2,0 대 0,1,0)
     미디어 쿼리 순서와 무관하게 이긴다. 그래서 목록은 44px 레인 안에서
     `left: 12px; right: 12px`을 먹고 20px로 줄었다가 `min-width`에 걸려 44px로
     섰다. 텍스트 자산으로만 보는 테스트는 `monitor.css`에 그 줄이 그대로
     있으니 계속 통과했다([[measured-does-not-mean-verified]]).

     여기서 다시 잡는다 — 레인을 기준으로 두되 화면 끝까지 내민다. 목록이
     보일 때 레인은 늘 열려 있고(`search.js`가 초점 밖에서 닫는다), 열린 레인의
     오른끝은 **셸 여백 16px**이다. 12px 여백까지 오려면 4px을 밖으로 내민다.
     단추 수를 세던 옛 값(120px)은 단추가 자리째 비키면서 틀린 셈이 됐다.

     실측(2026-09-20, "샌디"): 목록 폭 44 → 336·366·388px · 이름 칸 0 →
     178·208·230px. 561~760px은 건드리지 않는다 — 거기서는 목록이 펼친
     검색칸에 오른쪽으로 맞아 떨어져 원래 화면 안이었다(700px 실측 249.6px). */
  @media (max-width: 560px) {
    .world .site-search-panel {
      left: auto;
      right: calc(12px - 16px);
      width: calc(100vw - 24px);
    }
  }

  .world .site-search-field {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 4;
    width: var(--search-rest);
    height: 44px;
    padding: 0 12px;
    border-color: transparent;
    background: transparent;
    border-radius: 14px;
    /* **폭은 전이하지 않는다** (운영자 2026-09-20: "검색 누를때 모션도 마음에
       안들어"). 옛 판은 왼쪽으로 늘어나는 **동시에** 오른끝이 단추 위로
       미끄러져 두 방향으로 움직였다. 이제 칸은 첫 프레임부터 제자리에 서고
       면·선·그림자만 120ms 동안 든다. */
    transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
  }
  .world .site-search:focus-within .site-search-field { width: 100%; background: var(--panel); border-color: var(--line-strong); box-shadow: 0 10px 30px rgba(var(--shadow-ink),.12); }
  .world .site-search-input { opacity: 0; width: 0; padding: 0; transition: opacity 120ms ease; }
  .world .site-search:focus-within .site-search-input { opacity: 1; width: 100%; }
  .world .site-search-ico { flex: 0 0 auto; }
  .world .site-search-ico { width: 22px; height: 22px; color: var(--ink); stroke-width: 2.4; }
  /* 검색이 열린 동안 **단추가** 물러선다(이름이 아니라). 흐리는 것이 아니라
     `display: none`으로 **자리째** 비킨다 — 자리를 남겨 두면 레인이 그만큼
     못 자라서 왼끝이 다시 로고 쪽으로 밀린다. 폭을 전이하지 않으므로 줄이
     줄었다 느는 것도 한 프레임에 끝난다.

     **`.mast-actions`가 아니라 그 안의 단추들이다.** `search.js`가 검색 폼을
     `.mast-actions`에 `prepend`하므로(파일 첫 주석에 적혀 있다), 줄 통째로
     건드리면 검색칸과 자동완성 목록까지 같이 사라진다 — 실측에서 화면에
     로고만 남았다. */
  .world .masthead:has(.site-search:focus-within) .mast-actions .icon-button {
    display: none;
  }
  /* **상자도 구분선도 없다** (운영자 2026-09-17: "네모난 박스 그려진거 디자인
     안예뻐"). 테마·언어 단추는 마스트헤드에서 아이콘 둘일 뿐이고, 선을 그으면
     그 둘이 머리말 안의 또 다른 판처럼 읽힌다. 눌리는 자리는 44px 그대로 두고
     눌렸을 때만 면이 든다. */
  .world .icon-button {
    min-width: 44px;
    height: 44px;
    padding: 0 10px;
    border: 0;
    border-radius: var(--r-md);
    background: transparent;
    color: var(--ink-2);
  }
  /* `:hover`를 폰에 걸면 탭한 뒤에도 면이 남아, 방금 누른 단추가 **켜진 상태**로
     읽힌다(운영자 화면에서 해 아이콘에 동그란 면이 박혀 있었다). 포인터가 진짜
     있는 기기에만 건다. 눌린 순간은 `:active`가 말한다. */
  @media (hover: hover) {
    .world .icon-button:hover { background: var(--water-1); color: var(--ink); }
  }
  .world .icon-button:active { background: var(--water-1); color: var(--ink); }

  .page-landing .tape { display: none; }
  .page-landing .shell {
    width: 100%;
    padding: 0 10px 48px;
  }
  .page-landing .home-values {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    min-width: 0;
  }

  /* The hero is one answer, not a dashboard header plus six detached widgets. */
  .page-landing .seg-readout {
    order: 1;
    position: relative;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0;
    min-height: 220px;
    margin: 0 0 18px;
    padding: 17px 16px 10px;
    overflow: hidden;
    border: 0;
    border-radius: 18px;
    background:
      radial-gradient(120% 85% at 100% 0%, rgba(39, 115, 248, .14), transparent 58%),
      linear-gradient(180deg, var(--water-1) 0%, var(--water-3) 100%);
    box-shadow: 0 12px 32px rgba(var(--shadow-ink), .08);
  }
  /* Session and freshness stand above the value, in flow. Absolute top-right
   * looked like the comp but ran the closed-market badge ("국내장 마감 · 개장까지
   * 약 13시간 34분", 214px) 17px into the index name at 360px. */
  .page-landing .seg-status {
    order: 1;
    position: static;
    z-index: 3;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    min-height: 22px;
    gap: 6px 8px;
    margin: 0 0 8px;
  }
  .page-landing .seg-readout .session-badge {
    min-height: 22px;
    font-size: 12px;
    letter-spacing: 0;
  }
  .page-landing .seg-fresh { margin-left: 0; font-size: 12px; color: var(--ink-3); }
  .page-landing .seg-hero {
    order: 2;
    position: relative;
    z-index: 1;
    display: grid;
    width: 100%;
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    min-height: 162px;
    color: inherit;
  }
  .page-landing .seg-hero-head { width: auto; }
  .page-landing .seg-hero-name {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding-right: 0;
    font-size: 22px;
    font-weight: 750;
    line-height: 1.3;
  }
  .page-landing .seg-hero-context { font-size: 13px; font-weight: 550; color: var(--ink-2); }
  .page-landing .seg-hero-line {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 4px;
    margin-top: 13px;
  }
  .page-landing .seg-hero-level {
    order: -1;
    font-size: 42px;
    line-height: 1.05;
    font-weight: 760;
    letter-spacing: -.04em;
    color: var(--ink);
  }
  .page-landing .seg-hero-value {
    font-size: 17px;
    line-height: 1.2;
    font-weight: 720;
    letter-spacing: -.02em;
  }
  .page-landing .seg-hero-label {
    max-width: 48%;
    margin-top: 4px;
    font-size: 12px;
    line-height: 1.35;
    letter-spacing: 0;
    color: var(--ink-2);
  }
  .page-landing .ocean-hero-chart {
    position: absolute;
    right: -4px;
    top: 74px;
    bottom: auto;
    width: 44%;
    height: 86px;
    color: var(--accent);
    opacity: .9;
    pointer-events: none;
  }
  .page-landing .ocean-hero-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
  .page-landing .ocean-hero-chart .area { fill: currentColor; opacity: .12; }
  .page-landing .ocean-hero-chart .line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
  }
  .page-landing .ocean-hero-chart.up { color: var(--up); }
  .page-landing .ocean-hero-chart.down { color: var(--down); }
  .page-landing .ocean-hero-chart.flat { color: var(--ink-3); }

  .page-landing .ocean-range {
    order: 3;
    position: relative;
    z-index: 2;
    display: grid;
    width: 100%;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 4px;
    margin-top: auto;
  }
  .page-landing .ocean-range button {
    position: relative;
    min-width: 0;
    min-height: 32px;
    padding: 0 4px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--ink-3);
    font: 650 12px/1 var(--sans);
    cursor: pointer;
  }
  .page-landing .ocean-range button[aria-pressed="true"] {
    background: var(--panel);
    color: var(--accent);
    box-shadow: 0 2px 10px rgba(var(--shadow-ink), .10);
  }
  .page-landing .ocean-range button:disabled { opacity: .32; cursor: default; }
  /* The chip stays 32px so the hero keeps its proportions; the touch target is
   * 44px (6px above and below) through a pseudo-element the button owns. */
  .page-landing .ocean-range button::before { content: ""; position: absolute; inset: -6px 0; }
  .page-landing .seg-announce { order: 4; }

  .page-landing .market-heading {
    order: 2;
    align-items: center;
    margin: 0 6px 12px;
  }
  .page-landing .market-heading .lede-title {
    display: flex;
    align-items: baseline;
    gap: 7px;
    font-size: 20px;
    line-height: 1.3;
    font-weight: 760;
    letter-spacing: -.025em;
    transform: none;
  }
  .page-landing .lede-title-when { font-size: 12px; font-weight: 600; color: var(--ink-3); }
  .page-landing .my-board-edit {
    position: relative;
    width: 32px;
    min-height: 32px;
    padding: 0;
    border-radius: 12px;
    background: transparent;
    border-color: transparent;
    color: var(--ink-3);
    transform: none;
  }
  .page-landing .my-board-edit .picker-toggle-label { display: none; }
  /* 기호를 7px 오른쪽으로 밀어 두었던 줄을 걷었다. 32px 단추 안에서 `+`와 `−`가
     오른쪽으로 치우쳐 보인 원인이고(운영자 2026-09-17 "균형 안 맞는데?"), 단추는
     이미 `justify-content: center`라 밀 이유가 없다 — 실측 dx +7.0 → 0.0. */
  .page-landing .my-board-edit .ico { width: 22px; height: 22px; stroke-width: 1.8; }
  .page-landing .my-board-edit::after { content: ""; position: absolute; inset: -6px; }

  /* ── 제목줄의 단추 둘 ──────────────────────────────────────────────────
   *
   * 운영자 2026-09-18(다크 폰): *"+ 버튼이 왜 왼쪽으로 왔어. 그리고 편집 버튼
   * 아직도 잘리는데?"* 한 줄에 선 형제 둘이 서로 다른 이유로 깨져 있었다.
   *
   * **`+`는 내가 밀어냈다.** 보드 제목 옆 문구를 "추이선은 최근 30일"에서
   * "추이선은 옆의 등락률과 같은 기간"으로 늘렸더니(같은 날 앞선 작업) 그 문구를
   * 품은 `h1`이 354px를 다 차지했고, `flex-wrap: wrap`인 줄에서 단추가 **세 번째
   * 줄 왼쪽 끝**으로 떨어졌다(실측 390px: 단추 x=18, y=492 · 제목 y=435).
   * 줄을 안 접고 `h1`을 줄인다 — 문구는 `h1` 안에서 접히고 단추는 제자리에 선다.
   *
   * **`편집`은 상자보다 글자가 크다.** 폰에서 아이콘만 두려고 `width: 32px`에
   * `.picker-toggle-label { display: none }`을 걸었는데, 이 단추의 글자는 그
   * 클래스가 아니라 **맨 텍스트 노드**다(`console.js`가 `createTextNode`로 붙인다).
   * 규칙이 닿지 않으니 32px 상자에 38px 내용이 들어가 8px이 밖으로 샜고, 둥근
   * 모서리 밖으로 삐져나온 글자가 잘린 것처럼 보였다.
   *
   * 그래서 둘 다 **글자를 보여 주고 내용만큼 넓어진다.** 아이콘만 남긴 `+`가
   * 무슨 단추인지 묻게 만든 것이 이 제보의 절반이기도 하다. */
  /* 제목·설명·단추를 **한 격자**에 놓는다. 셋이 `h1` 안팎으로 나뉘어 있어
     (설명은 `h1` 속의 `span`이다) 흐름만으로는 "제목 옆에 단추, 설명은 아래 한 줄"이
     안 나온다 — 단추를 `h1` 옆에 두면 설명 옆에 서고, 설명은 좁아진 폭에서 세 줄이
     되어 마지막에 "기간" 한 낱말만 남았다.
     `display: contents`로 `h1`의 자식을 격자 칸에 직접 앉힌다. 크롬 89·사파리 15부터
     이 값이 제목 의미를 지운다는 옛 결함은 고쳐졌고, 접근성 트리에 heading이 그대로
     서는 것을 확인했다(2026-09-18). */
  .page-landing .market-heading {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: var(--sp-2);
    row-gap: 2px;
  }
  .page-landing .market-heading > .lede-title { display: contents; }
  .page-landing .market-heading > .lede-title > :first-child { grid-column: 1; grid-row: 1; }
  .page-landing .market-heading > .lede-title > .lede-title-when { grid-column: 1 / -1; grid-row: 2; }
  .page-landing .market-heading > .my-board-edit { grid-column: 2; grid-row: 1; }
  .page-landing .my-board-edit {
    flex: 0 0 auto;
    width: auto;
    min-width: 32px;
    padding: 0 10px;
    gap: 5px;
    white-space: nowrap;
  }
  .page-landing .my-board-edit .picker-toggle-label { display: inline; }
  .page-landing .market-heading #pin-picker-toggle {
    min-width: 0;
    min-height: 36px;
    padding: 0 12px;
    border-color: var(--accent);
    border-radius: 10px;
    background: var(--accent);
    color: var(--accent-ink);
  }
  .page-landing .market-heading #pin-picker-toggle .ico { width: 16px; height: 16px; }
  .page-landing .market-heading #pin-picker-toggle[aria-expanded="true"] {
    border-color: var(--line-strong);
    background: var(--panel-2);
    color: var(--ink);
  }
  .page-landing #pin-picker { order: 3; }
  .page-landing #pin-live { order: 4; }
  .page-landing .my-board-zone { order: 5; }
  .page-landing .board-basis {
    order: 9;
    margin: 8px 6px 0;
    color: var(--ink-2);
    font-size: 12px;
    line-height: 1.45;
  }
  .page-landing #board-state { order: 7; }
  .page-landing #board {
    order: 8;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    margin: 0;
    border-top: 0;
  }
  /* The board is one surface; rows are separated by hairlines, as the contract
   * asked ("테두리보다 행 구분이 우선인 목록"), not by six drawn boxes. */
  .page-landing :is(#board, #my-board) {
    gap: 0;
    padding: 2px 0;
    border-radius: 16px;
    background: var(--panel);
    box-shadow: var(--surface-shadow);
  }
  .page-landing #my-board > :is(.my-board-basis, .my-board-split) { padding: 6px 12px; margin: 0; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing):not(:last-child) { border-bottom: 1px solid var(--line); }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) {
    display: grid;
    grid-row: auto;
    grid-template-columns: minmax(0, 1fr) 132px 88px;
    grid-template-rows: 1fr 1fr;
    gap: 1px 7px;
    min-height: 70px;
    padding: 9px 12px;
    overflow: hidden;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing):hover { background: var(--water-1); transform: none; box-shadow: none; }

  /* ── 편집 중인 타일 ────────────────────────────────────────────────────
   *
   * 운영자 2026-09-18: *"모바일에서 내 종목 추가 부분 디자인 깨지는거 수정좀,
   * 박스가 깨짐."*
   *
   * 이 보드의 폰 규칙이 **전부** `:not(.tile-editing)`이다. 값이 있는 타일을
   * 한 줄로 눕히는 배치가 편집 중에는 맞지 않아서 그렇게 갈라 놓았는데, 그
   * 대가로 편집 타일은 폰에서 **아무 배치도 못 받는다** — 넓은 카드를 전제로
   * 짠 세로 배치가 그대로 서고, `.is-remove`의 `margin-left: auto`가 366px
   * 카드에서 화살표와 ×를 양 끝으로 밀어 가운데에 **190px 구멍**을 냈다
   * (실측 390px: 카드 높이 120px, → 오른끝 124px, × 왼끝 318px).
   *
   * 여기서는 한 줄이다: 손잡이·얼굴·이름이 왼쪽, 단추 셋이 오른쪽. 이름이 그
   * 구멍을 채우므로 밀어낼 여백 자체가 없어진다.
   *
   * **×는 여전히 떼어 놓는다.** 폰에는 마우스가 없어 쉬는 상태의 단추 셋이
   * 똑같이 생겼다는 것이 `segment.css`에 적힌 이유이고, 그 사정은 한 줄이 되어도
   * 그대로다. 자동 여백 대신 화살표 사이(8px)의 두 배가 넘는 고정 간격을 준다.
   * 색으로 가르지 않는 이유도 그대로다 — 이 세계에서 빨강은 "내렸다"이다. */
  .page-landing :is(#board, #my-board) > .tile.tile-editing {
    display: flex; flex-direction: row; align-items: center;
    gap: var(--sp-2); min-height: 0; padding: 10px 12px;
    border-radius: 0; border: 0; background: transparent;
  }
  .page-landing :is(#board, #my-board) > .tile.tile-editing:not(:last-child) {
    border-bottom: 1px solid var(--line);
  }
  /* 집어 든 판만 면과 테두리로 말한다 — 나머지는 보드 한 장 위의 줄이다. */
  .page-landing :is(#board, #my-board) > .tile.tile-editing.is-lifted {
    background: var(--panel-2); border: 1px solid var(--accent); border-radius: var(--r-md);
  }
  .page-landing .tile-editing > .tile-head { flex: 1 1 auto; min-width: 0; gap: var(--sp-2); }
  .page-landing .tile-editing .tile-name {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 15px; font-weight: 700;
  }
  .page-landing .tile-editing .tile-tag { display: none; }
  .page-landing .tile-editing > .tile-edit-controls { flex: 0 0 auto; }
  .page-landing .tile-editing .tile-edit-button.is-remove { margin-left: 18px; }

  /* '종목 추가'도 같은 함정이다. `.tile` 클래스를 달고 있어서 위의 격자 규칙이
     잡아가고(`:not(.tile-editing)`이 이것까지 막지는 않는다), 가운데로 모아 둔
     아이콘과 글자가 격자 칸으로 흩어진다 — 실측 390px에서 `+`는 x24, 글자는
     x183이라 단추 하나가 아니라 둘로 읽혔다. 제 배치를 돌려준다. */
  .page-landing :is(#board, #my-board) > .tile.tile-add {
    display: flex; flex-direction: row; align-items: center; justify-content: center;
    gap: var(--sp-2); min-height: 0; padding: 12px;
  }
  /* Global rows (S&P 500, BTC, ETH) stay on the phone: the board is the 24-hour
   * market across three regions, not a Korea-only list. Ghost cells are grid
   * padding and have nothing to pad in one column. */
  .page-landing #board > .tile.is-ghost { display: none; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-head { grid-column: 1; grid-row: 1; align-self: end; align-items: center; min-width: 0; z-index: 1; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-name { font-size: 15px; font-weight: 700; line-height: 1.35; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-tag { display: none; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-value {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 44px;
    width: 100%;
    justify-self: stretch;
    z-index: 1;
    font-size: 17px;
    line-height: 1.2;
    font-weight: 650;
    letter-spacing: 0;
  }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-value > :first-child { justify-self: end; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-unit { justify-self: start; font-size: 12px; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-delta {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    justify-self: end;
    margin-right: 44px;
    flex-wrap: nowrap;
    gap: 6px;
    z-index: 1;
  }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-change { font-size: 14px; font-weight: 720; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-basis { display: none; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-delta > .tile-basis { flex-basis: auto; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-anchor {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
    align-self: start;
    max-width: 100%;
    margin: 0;
    flex-wrap: nowrap;
    white-space: nowrap;
    font-size: var(--fs-xs);
    line-height: 1.45;
    z-index: 1;
  }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-foot {
    position: relative;
    right: auto;
    bottom: auto;
    grid-column: 3;
    grid-row: 1 / span 2;
    align-self: center;
    width: 88px;
    height: 42px;
    margin: 0;
    /* .72 read 1.66:1 light / 1.87:1 dark on the row (reviewer, 2026-09-16);
     * 1.4.11 wants 3:1 for the line that is the row's reason to exist — and for
     * the pixels, not the computed stroke: a 1.4px line at DSF 2 is mostly
     * anti-aliased edge, so the line is 2px and opaque. */
    opacity: 1;
  }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-spark { height: 42px; padding: 0; gap: 0; }
  /* **기간은 줄마다 적지 않는다** (운영자 2026-09-17: "삼전·하이닉스 옆에 모바일
     에서 30일 표시 빼줘, 너무 좁아 보여").

     라벨을 되살린 것은 "30일인지 1일인지 헷갈린다"는 같은 사람의 말 때문이었다.
     둘 다 맞다 — 창은 말해야 하고, 여덟 줄에 여덟 번 말할 필요는 없다.
     보드 제목이 한 번 말한다(`console.scope`: "… · 추이선은 최근 30일").
     줄은 폭을 돌려받고, 답은 여전히 화면에 있다. */
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing)
    :is(.tile-spark[data-span="session"], .tile-spark[data-span="24h"]) .spark-period { display: none; }
  .page-landing :is(#board, #my-board) > .tile:not(.tile-editing) .tile-spark path.fill { fill: currentColor; opacity: .06; }

  /* The comp's first viewport carries the four quick entries; with the six-row
   * board (operator rule) they had slipped under the fold. They follow the hero
   * as one compact row of surfaces (reviewer fix 3, 2026-09-16). */
  .page-landing .ocean-quick {
    order: 1;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px;
    margin: 0 0 18px;
  }
  .page-landing .ocean-quick a {
    min-width: 0;
    min-height: 64px;
    padding: 8px 4px 7px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    border: 1px solid transparent;
    border-radius: 14px;
    background: var(--panel);
    box-shadow: var(--surface-shadow);
    color: var(--ink-2);
    text-decoration: none;
    font-size: 12px;
    font-weight: 650;
    white-space: nowrap;
  }
  .page-landing .ocean-quick svg { width: 20px; height: 20px; color: var(--accent); }
  .page-landing .disclose { order: 11; margin-top: 10px; }
  /* Wide tables keep their sticky first column, but the edge that says "the
   * numbers pass underneath" is a pale water shadow, not the dark world's black. */
  .world .accessible-table th:first-child,
  .world .accessible-table td:first-child {
    border-right-color: var(--line);
    box-shadow: 6px 0 10px -6px rgba(var(--shadow-ink), .12);
  }
  /* The depth wash may not reach past the 10px shell gutter: at -14px it put the
   * document 2px wider than the phone (scrollWidth 392 on a 390 viewport). */
  /* "결과 도달 2026-09-14" broke inside the date under the cell's
   * overflow-wrap: anywhere; the pill wraps at its own space instead. */
  .world .score-page .score-landed {
    display: block;
    width: fit-content;
    max-width: 100%;
    margin: 4px 0 0;
    white-space: normal;
    overflow-wrap: normal;
  }
  .world .score-page .score-landed-on { white-space: nowrap; }

  /* 화면 위에 떠 있는 알약. 가장자리에 붙은 띠는 화면의 끝처럼 읽히고,
     떠 있는 바는 **내용 위에 놓인 조작부**로 읽힌다 — 이 서비스가 먼저 쓰던
     문법이고 운영자가 그것을 다시 골랐다. 담긴 링크는 그대로 다섯이다. */
  .world .view-switch {
    left: var(--pagebar-inset);
    right: var(--pagebar-inset);
    bottom: calc(var(--pagebar-inset) + env(safe-area-inset-bottom, 0px));
    width: auto;
    height: var(--pagebar-h);
    padding: 0 4px;
    overflow: visible;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--panel-2);
    box-shadow: 0 10px 30px rgba(var(--shadow-ink), .14);
  }
  .world .view-tab { gap: 3px; border-radius: var(--r-pill); color: var(--ink-3); }
  .world .view-tab.active { color: var(--accent); background: transparent; }
  .world .view-tab-ico { width: 21px; height: 21px; }
  /* The bar names the same five places as the desktop tabs and the comp: "AI 리그"
   * and "종목 검색", not their clipped forms. Five full names fit at 360px. */
  /* 라벨 하한선은 12px이다(tokens.css) — 하단 바의 이름이 유일하게 11px로
     남아 있었다. 다섯 이름이 360px에 그대로 들어간다(실측 2026-09-17). */
  .world .view-tab-name { display: block; font-size: var(--fs-xs); font-weight: 620; }
  .world .view-tab-name.is-short { display: none; }
  .world .view-tab.active .view-tab-name { font-weight: 740; }

  /* Mobile is the canonical product, not a compressed desktop dashboard. */
  .world:not(.page-landing) .tape { display: none; }
  .world .shell {
    width: 100%;
    padding-left: 12px;
    padding-right: 12px;
    /* 첫 카드가 머리말 선에 **0px으로 맞닿아** 있었다(실측 390px: 바 아래끝 64,
       카드 위끝 64). 운영자가 "메인 카드랑 위 바가 너무 겹친다"고 본 자리다.
       바를 더 줄이는 것은 답이 아니다 — 64px 안에 선 것은 44px 조작부 한 줄이고
       위아래가 10·11px이라 이미 꽉 찼다. 모자란 것은 **바 아래의 숨**이다.
       옆 여백과 같은 12px을 준다. */
    padding-top: 12px;
    padding-bottom: 42px;
  }
  .world .console-section { margin-top: 40px; }
  .world .console-section:first-child { margin-top: 24px; }
  .world .console-section > header {
    align-items: flex-start;
    margin-bottom: 12px;
  }
  .world .console-section > header :is(h1, h2) {
    font-size: 22px;
    line-height: 1.32;
  }
  .world .console-section > header .sec-note {
    width: 100%;
    margin-top: -3px;
    font-size: 13px;
    line-height: 1.55;
  }
  .world :is(.panel, .stock-section, .asset-section, .coin-section, .methodology) {
    border-radius: 16px;
  }

  /* One readable card per row. Existing two-column grids forced the copy and
   * numeric basis below comfortable mobile sizes. */
  .world :is(.kro-grid, .board, .crypto-grid, .cboard-grid, .cliq-grid,
    .weekend-grid, .method-grid) {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
  .world :is(.metric-card, .kro-card, .cliq-card) {
    min-height: 0;
    padding: 16px;
    border-radius: 14px;
  }
  .world :is(.metric-card, .kro-card, .cliq-card) .metric-primary {
    font-size: clamp(24px, 8vw, 32px);
  }
  .world :is(.metric-card, .kro-card, .cliq-card) .chart-slot {
    min-height: 72px;
  }
  .world :is(.metric-description, .metric-hints, .kro-method, .interpretation-note) {
    font-size: 13px;
    line-height: 1.6;
  }

  /* The first market board is the page answer: its heading stands in the flow
   * and the cards sit straight on the canvas. A card-shaped header wrapped
   * around card children read as a card inside a card. */
  .world :is(#kr-overnight, #us-board, #crypto-tape) > header {
    margin: 0 0 12px;
    /* 2px이던 자리. 첫 보드의 제목만 폰에서 판 글 열 밖에 서 있었다(실측
       2026-09-17: 제목 14 · 판 글 24). 카드가 캔버스에 곧게 앉는다는 뜻은
       그대로이고, 제목 글자만 다른 섹션과 같은 열로 온다. */
    padding: 0 0 0 var(--panel-inset);
  }
  .world :is(#kr-overnight, #us-board, #crypto-tape) .state-block {
    margin: 0;
  }

  /* Detail pages use a familiar app hierarchy: identity, live value, basis,
   * compact tabs, then one vertical reading path. */
  .world :is(.stock-head, .asset-head, .coin-head) {
    min-height: 78px;
    padding: 14px 2px 12px;
    /* 단축 뒤에 다시 적는다. `--pin-reserve`는 상세 페이지가 **절대 위치 핀
       단추 밑에 이름이 흐르지 않게** 잡아 둔 자리인데(stock.html), 위 `padding`
       단축이 그것을 지워 `Nasdaq` 칩이 단추 밑으로 15px 들어갔다(실측 390px,
       2026-09-20). 변수가 없는 페이지는 2px 그대로다. */
    padding-right: var(--pin-reserve, 2px);
  }
  .world :is(.quote, .asset-readout, .coin-readout) {
    margin-inline: 0;
    padding: 18px 16px;
    border-radius: 18px;
  }
  .world :is(.quote-num, .quote-live-num, .asset-price, .coin-price) {
    font-size: clamp(36px, 11vw, 48px);
    line-height: 1.08;
  }
  .world .sec-nav {
    position: sticky;
    top: var(--mast-h);
    z-index: 90;
    margin-inline: -12px;
    padding-inline: 12px;
    overflow-x: auto;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    backdrop-filter: blur(14px);
    scrollbar-width: none;
  }
  .world .sec-nav::-webkit-scrollbar { display: none; }
  .world .stock-tab { flex: 0 0 auto; min-width: 68px; min-height: 48px; }
  .world :is(.stock-section, .asset-section, .coin-section) {
    margin-top: 16px;
    padding: 16px;
  }

  /* Records and feeds become a quiet stream. Each item is a full-width touch
   * target; secondary provenance stays visible below it. */
  .world :is(.news-page, .markets-page, .weekend-page, .score-page, .league-page,
    .glossary) { padding-top: 4px; }
  .world .news-list > *,
  .world .news-panel,
  .world .weekend-answer,
  .world .flow-step { border-radius: 14px; }
  .world .news-list a { min-height: 44px; }
  .world .news-filter,
  .world .bio-filter,
  .world .scr-tabs {
    display: flex;
    gap: 7px;
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
  }
  .world .news-filter::-webkit-scrollbar,
  .world .bio-filter::-webkit-scrollbar,
  .world .scr-tabs::-webkit-scrollbar { display: none; }

  .world .league-page .console-section:first-child {
    display: block;
    margin-top: 12px;
    padding: 20px 16px;
    border-radius: 20px;
  }
  .world .league-page .console-section:first-child > header {
    padding: 0;
    border: 0;
    background: transparent;
  }
  .world .league-page #league-title { font-size: 28px; }
  /* **상자를 걷는다** (운영자 2026-09-17: "버튼 디자인이 이상해 그 아래 글이랑
     align도 안맞고"). 상자에 12px 안 여백이 들어 있어서 첫 글자가 아래 문단보다
     12px 오른쪽에서 시작했다 — 상자는 맞았고 글자가 어긋났다. 데스크톱은 이미
     밑줄 링크 한 줄이다. 폰도 같은 문법을 쓰면 글자가 같은 열에 서고, 이 줄이
     제목과 문단 사이의 **차례**로 읽힌다(버튼 넷이 아니라).
     아래 여백이 없어 h2에 맞붙어 있던 것도 함께 푼다. */
  .world .league-page .league-jump {
    display: flex;
    gap: 4px 18px;
    margin: 14px 0 20px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .world .league-page .league-jump::-webkit-scrollbar { display: none; }
  .world .league-page .league-jump a {
    flex: 0 0 auto;
    min-height: 40px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    border: 0;
    border-bottom: 1px solid var(--water-line);
    border-radius: 0;
    background: transparent;
    font-size: var(--fs-sm);
    /* 기본 규칙의 `min-width: 44px` + 가운데 정렬이 짧은 이름("규칙")에
       6px 여백을 만들어 첫 글자를 다시 밀어냈다. 표적 높이는 40px이 진다. */
    min-width: 0;
    justify-content: flex-start;
  }
  .world .league-page .league-contract-route {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: 16px;
    border: 1px solid var(--water-line);
    border-radius: 14px;
    overflow: hidden;
  }
  .world .league-page .league-contract-route > div {
    min-width: 0;
    padding: 12px;
    border-color: var(--water-line);
  }
  .world .league-page .league-rule { display: block; padding: 18px 16px; }
  .world .league-page .league-rule > * { margin-top: 10px; }

  .world .lookup { padding: 16px; border-radius: 18px; }
  .world .lookup-field { min-height: 54px; border-radius: 15px; }
  .world .lookup-input { font-size: 16px; }
  .world .scr-controls { grid-template-columns: minmax(0, 1fr); }

  .world .table-scroll {
    margin-inline: -1px;
    border-radius: 13px;
    overscroll-behavior-inline: contain;
  }
  .world .score-page .score-records .table-scroll { overflow-x: visible; }
  /* One record per row, read top-down: stock (with its status pill), reporter,
   * the current reference on the right, then the dated facts and the three
   * horizons as a labelled line. Nothing is hidden and no word breaks mid-way. */
  .world .score-page .score-table { display: block; width: 100%; min-width: 0; }
  .world .score-page .score-table caption { display: block; }
  .world .score-page .score-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
  }
  .world .score-page .score-table tbody { display: block; }
  .world .score-page .score-table tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 10px;
    padding: 12px;
    border-bottom: 1px solid var(--line);
  }
  /* :nth-child(n) only raises specificity above world.css's cell rule
   * (max-width 9.5em, overflow-wrap anywhere) so the row grammar wins. */
  .world .score-page .score-table tbody tr > :nth-child(n) {
    position: static;
    display: block;
    width: auto;
    min-width: 0;
    max-width: none;
    padding: 0;
    border: 0;
    box-shadow: none;
    white-space: normal;
    overflow-wrap: normal;
    word-break: keep-all;
    text-align: left;
  }
  .world .score-page .score-table tbody tr > :nth-child(1) {
    order: 1;
    flex: 1 1 55%;
    font-size: 15px;
    font-weight: 700;
    color: var(--ink);
  }
  .world .score-page .score-table tbody tr > :nth-child(6) {
    order: 2;
    flex: 0 0 auto;
    margin-left: auto;
    font-size: 15px;
    font-weight: 700;
    text-align: right;
  }
  /* `small`은 부모의 0.8배라 12.2px 칸 안에서 9.8px이 됐다(실측 2026-09-17).
     라벨 하한선은 12px이다 — 크기를 토큰으로 못 박는다. */
  .world .score-page .score-table tbody tr > :nth-child(6) small { display: block; font-size: var(--fs-xs); font-weight: 500; color: var(--ink-3); }
  .world .score-page .score-table tbody tr > :nth-child(2) {
    order: 3;
    flex: 1 1 100%;
    font-size: 13px;
    color: var(--ink-2);
  }
  .world .score-page .score-table tbody tr > :is(:nth-child(3), :nth-child(4), :nth-child(5),
    :nth-child(7), :nth-child(8), :nth-child(9)) {
    order: 4;
    flex: 0 0 auto;
    font-size: 12px;
    line-height: 1.4;
    color: var(--ink-3);
    white-space: nowrap;
  }
  /* The three horizons start their own line: a full-width empty flex item sits
   * between the dated facts and the horizons, so no single horizon is orphaned. */
  .world .score-page .score-table tbody tr::after { content: ""; order: 5; flex: 1 0 100%; height: 0; }
  .world .score-page .score-table tbody tr > :is(:nth-child(7), :nth-child(8), :nth-child(9)) { order: 6; }
  .world .score-page .score-table tbody tr > :is(:nth-child(3), :nth-child(4), :nth-child(5),
    :nth-child(7), :nth-child(8), :nth-child(9))::before {
    margin-right: 3px;
    font-weight: 600;
  }
  .world .score-page .score-table tbody tr > :nth-child(3)::before { content: "공시"; }
  .world .score-page .score-table tbody tr > :nth-child(4)::before { content: "보유"; }
  .world .score-page .score-table tbody tr > :nth-child(5)::before { content: "기준가"; }
  .world .score-page .score-table tbody tr > :nth-child(7)::before { content: "+1개월"; }
  .world .score-page .score-table tbody tr > :nth-child(8)::before { content: "+3개월"; }
  .world .score-page .score-table tbody tr > :nth-child(9)::before { content: "+6개월"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(3)::before { content: "filed"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(4)::before { content: "stake"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(5)::before { content: "base"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(7)::before { content: "+1M"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(8)::before { content: "+3M"; }
  html:is([lang="en"], [data-lang="en"]) .world .score-page .score-table tbody tr > :nth-child(9)::before { content: "+6M"; }
  .world .score-page .score-table tbody tr > :nth-child(n) a {
    display: inline;
    overflow-wrap: normal;
    word-break: keep-all;
  }
  .world .score-page .score-table tbody tr > :is(:nth-child(3), :nth-child(4), :nth-child(5),
    :nth-child(7), :nth-child(8), :nth-child(9)) small {
    display: inline;
    margin-left: 3px;
  }
  .world .score-page .score-table small { display: block; margin-top: 3px; }
  .world .score-page .weekend-caption { padding: 12px; }

  .world .glossary .console-head { justify-content: flex-start; text-align: left; }
  .world .glossary .console-h1-sub { margin-left: 0; }
  .world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
    .score-table, .crypto-table) :is(th, td) {
    min-height: 44px;
    padding-top: 11px;
    padding-bottom: 11px;
  }
  .world .site-footer { padding-bottom: 24px; }
}

@media (min-width: 761px) {
  .page-landing .ask-body { align-self: stretch; }
  .page-landing .ask-meta { margin-top: auto; }
  .page-landing .news-videos .nvid-meta { min-height: 28px; }
  .page-landing .zone-links {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 24px;
  }
  .page-landing .zone-link-card:nth-last-child(-n + 2) { border-bottom: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .world *, .world *::before, .world *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ======================================================================
 * Carbon night. Dark mode is deliberately neutral: canvas, chrome and surfaces
 * use charcoal steps; colour is reserved for market direction and status.
 * The market colours keep their Korean meaning (red rises, blue falls) at
 * 4.5:1 or better on every surface.
 * Only tokens change here; rules above read the tokens. A stored choice
 * (localStorage mulmit.theme) or the system preference sets data-theme before
 * first paint from an inline script in every page head.
 * ================================================================== */
html[data-theme="dark"] .world {
  --bg: #090909;
  --bg-deep: #0d0d0d;
  --panel: #111111;
  --panel-2: #171717;
  --panel-3: #1d1d1d;
  --ink: #f3f3f3;
  --ink-2: #c3c3c3;
  --muted: #c3c3c3;
  --ink-3: #999999;
  --faint: #999999;
  --line: #252525;
  --line-strong: #363636;
  --line-2: #363636;
  --accent: #f2f2f2;
  --accent-ink: #090909;
  --accent-text: #ededed;
  --accent-soft: rgba(255, 255, 255, .10);
  --chart: #e8e8e8;
  --chart-fill: rgba(255, 255, 255, .10);
  --up: #ff687b;
  --down: #78a6ff;
  --green: #54d894;
  --red: #ff687b;
  --amber: #e8b45d;
  --cal-sun: #ff687b;
  --cal-sat: #78a6ff;
  --violet: #b39cff;
  --seg-ghost: #242424;
  --seg-ghost-2: #1d1d1d;
  --seg-live: #ededed;
  --shadow: 0 14px 40px rgba(0, 0, 0, .48);
  --shadow-ink: 0, 0, 0;
  --water-1: #0d0d0d;
  --water-2: #121212;
  --water-3: #181818;
  --water-4: #1e1e1e;
  --water-line: #2b2b2b;
  --chrome: #090909;
  --chrome-2: rgba(9, 9, 9, .78);
  --current: #d7d7d7;
  --current-2: #f2f2f2;
  --grade-a: #54d894;
  --grade-b: #78a6ff;
  --grade-c: #b39cff;
  --grade-d: #e8b45d;
  --grade-e: #ff687b;
  --surface-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 10px 28px rgba(0, 0, 0, .34);
  --surface-shadow-lift: 0 2px 4px rgba(0, 0, 0, .56), 0 16px 36px rgba(0, 0, 0, .42);
}
html[data-theme="dark"]:has(body.world) { --canvas: #090909; background: var(--canvas); }
html[data-theme="dark"] .world .ask-option { background: var(--water-2); }
html[data-theme="dark"] .world ::selection { background: rgba(255, 255, 255, .22); color: var(--ink); }
/* External SVG images cannot inherit `currentColor` from the page. The mark is
 * black by default, so invert it on the night canvas. */
html[data-theme="dark"] .world img.brand-mark { filter: invert(1); }
html[data-theme="dark"] .world .brand strong { color: var(--ink); }
html[data-theme="dark"] .page-landing .seg-readout {
  background:
    radial-gradient(120% 85% at 100% 0%, rgba(255, 255, 255, .08), transparent 58%),
    linear-gradient(180deg, var(--water-3) 0%, var(--water-1) 100%);
}
html[data-theme="dark"] .world .session-badge.open i { box-shadow: none; }

@media (max-width: 370px) {
  /* At 360 the 42px level ran 3.5px under the chart's left edge; the chart yields. */
  .page-landing .ocean-hero-chart { width: 40%; }
}

/* --- The first 120 ms ------------------------------------------------------
 * Before the first payload the hero showed only its badge and range chips and
 * the board a spinner in a 340px void, and "1일" flipped enabled → disabled as
 * data landed (reviewer, 2026-09-16). While html.booting, the hero draws its
 * title/value/chart silhouettes and the range waits unseen; the phone board
 * draws six row silhouettes in its own grammar. No motion: a skeleton is a
 * shape, not a spinner. */
html.booting .page-landing .seg-readout:has(#seg-hero[hidden]) { min-height: 250px; }
html.booting .page-landing .seg-readout:has(#seg-hero[hidden]) .ocean-range { visibility: hidden; }
html.booting .page-landing .seg-readout:has(#seg-hero[hidden])::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--water-3), var(--water-3)),
    linear-gradient(var(--water-3), var(--water-3)),
    linear-gradient(var(--water-3), var(--water-3)),
    linear-gradient(var(--water-2), var(--water-2));
  background-size: 132px 24px, 96px 14px, 210px 44px, 44% 86px;
  background-position: 16px 62px, 16px 94px, 16px 118px, calc(100% - 12px) 74px;
  background-repeat: no-repeat;
  opacity: .9;
}
html.booting .page-landing #board-state { display: none; }
@media (max-width: 760px) {
  html.booting .page-landing #board:empty {
    display: block;
    min-height: 428px;
    padding: 0;
    border-radius: 16px;
    background-color: var(--panel);
    background-image:
      linear-gradient(to bottom, transparent 18px, var(--water-3) 18px, var(--water-3) 32px, transparent 32px),
      linear-gradient(to bottom, transparent 41px, var(--water-2) 41px, var(--water-2) 51px, transparent 51px),
      linear-gradient(to bottom, transparent 16px, var(--water-3) 16px, var(--water-3) 34px, transparent 34px),
      linear-gradient(to bottom, transparent 41px, var(--water-2) 41px, var(--water-2) 51px, transparent 51px),
      linear-gradient(to bottom, transparent 70px, var(--line) 70px);
    background-size: 118px 71px, 84px 71px, 104px 71px, 72px 71px, 100% 71px;
    background-position: 12px 2px, 12px 2px, calc(100% - 118px) 2px, calc(100% - 118px) 2px, 0 2px;
    background-repeat: repeat-y;
    box-shadow: var(--surface-shadow);
  }
}

/* segment.css dims the desktop board sparkline SVG to .5 (.page-landing .tile-spark > svg),
 * which put the 30-day lines at 2.0–2.2:1 on the card; the line carries the row and
 * must clear 3:1 (WCAG 1.4.11). The .9 on .tile-spark stays as the only dimming. */
.world.page-landing .tile-spark > svg { opacity: 1; }
.world.page-landing .tile-spark { opacity: 1; }

/* 7. Desktop: the fixed section rail (43px) parked over the last visible row;
 * the page ends above it so every control can scroll clear. */
@media (min-width: 761px) {
  body.world:has(.section-rail) { padding-bottom: 72px; }
}

/* --- Operator round, 2026-09-16 evening ------------------------------------ */
/* Search: no blue box. Text inputs get :focus-visible even on a mouse click, so
 * the browser ring wrapped the field a second time. The field itself changes —
 * it widens and its ground lifts — and that is the focus state. */
.world .site-search-input:focus,
.world .site-search-input:focus-visible { outline: none; box-shadow: none; }
.world .site-search-field,
.world .site-search:focus-within .site-search-field { outline: none; box-shadow: none; }
.world .site-search:focus-within .site-search-field { border-color: var(--water-line); background: var(--water-1); }
/* Index tables side by side: a wrapped sector name or a two-line header threw the
 * rows of the two tables out of step. */
/* world.css wraps every non-numeric table cell at (0,4,1); the id lifts these two.
 * **폰에는 걸지 않는다.** 나란히 서는 것은 데스크톱뿐이고(#kridx-body는 520px
 * 이상에서만 두 열), 폰에서는 world.css V5-1이 칸을 9.5em(114px)으로 조여서
 * `코스피 200 커뮤니케이션서비스`(144px)가 옆 칸 위로 샜다(390px 실측 2026-09-20). */
@media (min-width: 761px) {
  .world #kridx-body .kridx-table thead th,
  .world #kridx-body .kridx-table tr > :first-child { white-space: nowrap; }
}
/* Underline tabs carry no track line: the hairline ran into the panel's rounded
 * corner below and read as a line poking out. The indicator is the only line. */
.world .segmented { box-shadow: none; }
/* Calendar tables: the last cell touched the panel edge. */
.world #kr-calendar .accessible-table :is(th, td):first-child { padding-left: var(--panel-inset); }
.world #kr-calendar .accessible-table :is(th, td):last-child { padding-right: var(--panel-inset); }
/* Grade table: a face before the name, names left-aligned like every other table. */
/* `nowrap`이었다. 이름 칸은 폰에서 9.5em(114px)으로 조여지는데(world.css V5-1)
   `현대코퍼레이션홀딩스`는 130px이라 **옆 칸 위로 샜다**(360·390px 실측
   2026-09-20, +9~40px). 이름은 접혀도 같은 이름이다. `a`가 inline-flex라
   max-content로 서므로 `max-width`를 같이 줘야 실제로 접힌다. */
.world #krtg-table th[scope="row"] { text-align: left; white-space: normal; }
.world #krtg-table th[scope="row"] a { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; }
.world #krtg-table th[scope="row"] .inline-logo { width: 18px; height: 18px; margin: 0; }
/* Breadth as a histogram — nine columns, down on the left, up on the right, the
 * count on top of each column (see monitor.js renderKrBreadth). */
.world .krbd-legend { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; margin: 10px 0 2px; font-size: 13px; color: var(--ink-2); }
.world .krbd-legend b { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.world .krbd-legend b.up { color: var(--up); }
.world .krbd-legend b.down { color: var(--down); }
.world .krbd-legend .krbd-total { margin-left: auto; font-size: 12px; color: var(--ink-3); }
.world #krbd-bars.krbd-hist { display: grid; grid-template-columns: repeat(9, minmax(0, 1fr)); gap: 8px; align-items: end; height: 210px; padding: 12px 0 4px; }
.world .krbd-col { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; min-width: 0; gap: 6px; }
.world .krbd-n { font: 650 12px/1 var(--num); font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
.world .krbd-bar { display: block; width: 100%; max-width: 64px; min-height: 3px; border-radius: 6px 6px 3px 3px; background: var(--line-strong); }
.world .krbd-bar.up { background: var(--up); }
.world .krbd-bar.down { background: var(--down); }
.world .krbd-x { font-size: var(--fs-xs); line-height: 1.2; color: var(--ink-3); text-align: center; white-space: nowrap; }
@media (max-width: 560px) {
  .world #krbd-bars.krbd-hist { gap: 4px; height: 170px; }
  /* 아홉 칸 전부에 12px 라벨을 세우면 360px에서 칸이 31px이라 넘친다. 크기를
     낮추는 대신(9.5px이었다) **양끝·양쪽 가운데·보합 다섯 칸만** 이름을 세운다.
     나머지 넷은 자리를 지키되 감춰 기둥 높이가 흔들리지 않고, 온전한 이름은
     `title`과 `aria-label`에 그대로 있다. */
  .world .krbd-x { font-size: var(--fs-xs); }
  .world .krbd-col:nth-child(2) .krbd-x,
  .world .krbd-col:nth-child(3) .krbd-x,
  .world .krbd-col:nth-child(7) .krbd-x,
  .world .krbd-col:nth-child(8) .krbd-x { visibility: hidden; }
}

/* --- /league: divisions, portfolio bands, grouped ledger --------------------- */
.world .league-group-title {
  display: flex; align-items: baseline; gap: 8px;
  margin: 0; padding: 14px var(--panel-inset) 8px;
  font-size: 14px; font-weight: 700; letter-spacing: -.01em; color: var(--ink);
}
.world .league-group-title + .league-group-title { margin-top: 0; }
.world .league-group-title span { font: 600 12px/1 var(--num); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.world .league-group-title + .table-scroll { margin-top: 0; }
/* **부문 사이는 벌린다** (운영자 2026-09-17: "규칙 6이랑 모델 테이블이랑 너무
   붙어있는데? 분리하면 안돼?"). 6px에 머리카락 선 하나로는 코스피 기준 줄 바로
   아래에 다음 부문 제목이 붙어, 두 순위표가 한 표의 두 구획처럼 읽혔다.
   선은 두고 위아래 숨을 준다 — 제목이 아래 표의 것임이 여백으로도 보여야 한다. */
.world .table-scroll + .league-group-title {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
}
/* 보유 도넛 (운영자 2026-09-17: "보기가 너무 어려운데 원형 포트폴리오로는").
 *
 * 띠 하나에 종목 열 개를 늘어놓던 옛 꼴은 조각이 3px이라 이름이 안 들어갔고,
 * 이름 없는 띠는 값이 아니라 장식이 된다. 고리는 조각을 **각**으로 말해 작은
 * 조각도 자리를 갖고, 값은 범례가 글자로 적는다.
 *
 * 조각 색은 파이썬이 조각마다 `--slice`로 준다 — 13F·국민연금 파이와 같은
 * 황금각 색상환이다(`monitor.js sliceColor`). 한 색의 밝기 사다리로 그렸다가
 * "색깔 톤이 다 비슷하다"는 말을 들은 자리다. 채도·명도는 묶여 있어 어느 조각도
 * 오름·내림으로 읽히지 않는다 — 색은 여기서 방향을 말하지 않는다.
 *
 * 잎사귀 선 대신 범례를 쓴 것은 서버가 글자 폭을 못 재기 때문이다: 360px에서
 * 라벨이 서로 겹치느니 줄로 세운다. */
.world .league-donut {
  display: grid; grid-template-columns: 104px minmax(0, 1fr);
  align-items: center; gap: var(--sp-4);
  margin: var(--sp-3) 0 var(--sp-3);
}
.world .league-donut-ring { width: 104px; height: 104px; transform: rotate(-90deg); }
.world .league-donut-arc {
  fill: none; stroke-width: 15; stroke: var(--slice, var(--water-3)); stroke-linecap: butt;
}
.world .league-donut-legend {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  /* **범례는 고리 안에 든다.** 줄 높이를 자동으로 두었더니 조각이 많은 장의
     범례가 130px이 되어 고리(104px)를 넘겼다. 도넛 판 높이가 장마다 달라지니
     그 아래 업종 표와 근거가 통째로 밀려, 같은 줄의 두 장이 26px 어긋났다
     (자 ALIGN, 실측 2026-09-18 pc1440). 최대 열한 조각(열 종목 + 현금)이 두 단
     이면 여섯 줄이고, 한 줄 17px이면 102px이라 고리 안에 든다 — 조각이 몇 개든
     판 높이는 104px 하나다. 짧은 범례는 고리 가운데에 선다. */
  grid-auto-rows: 17px; align-content: center;
  gap: 0 var(--sp-5); margin: 0; padding: 0; list-style: none; min-width: 0;
  /* 값이 칸 오른쪽 끝에 붙으므로 칸이 넓어질수록 이름과 값이 멀어진다 — 넓은
     카드에서 300px까지 벌어졌다. 두 단이 읽히는 폭에서 멈춘다. */
  max-width: 460px;
}
.world .league-donut-key {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); color: var(--ink-2); min-width: 0;
}
/* **점만 잡는다.** `> i`로 두었더니 얼굴 칸(`<i>`)까지 같이 잡혀 16px 자리가
   8px 점이 되었다 — 로고 없는 줄만 이름이 당겨져 범례가 어긋난 진짜 원인이다
   (실측 2026-09-17, 선택자 특이도 (0,2,1)이 (0,2,0)을 이겼다). 얼굴은 이제
   `<span>`이라 이 규칙이 닿지 않는다. */
.world .league-donut-key > i.league-donut-dot {
  flex: 0 0 auto; width: 8px; height: 8px; border-radius: 2px;
  background: var(--slice, var(--water-3));
}
/* 얼굴은 13F·국민연금 표가 쓰는 **같은 파일**이다 — 같은 회사가 화면마다 다르게
   생기면 안 된다. 로고가 없는 종목에는 아예 세우지 않는다(파이썬이 가른다). */
.world .league-donut-logo {
  flex: 0 0 auto; width: 16px; height: 16px; border-radius: 4px;
  object-fit: contain; background: var(--panel-2);
}
/* 얼굴이 없는 줄도 같은 자리를 잡는다 — 한 줄만 16px 당겨지면 범례가 어긋난다.
   종목에는 글자 판이 서고(`letter-mark`), 판을 새길 것이 없는 현금만 빈칸이다. */
.world .league-donut-logo.is-blank { background: none; }
.world .league-donut-logo.letter-mark { width: 16px; height: 16px; border-radius: 4px; }
.world .league-donut-logo.letter-mark > span { font-size: 9px; }
.world .league-donut-name {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.world .league-donut-key b {
  flex: 0 0 auto; color: var(--ink); font: 700 var(--fs-xs)/1.5 var(--num);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 560px) {
  /* 폰에서는 고리가 위, 범례가 아래다 — 104px 고리 옆에 남는 폭으로는 이름과
     비중이 한 줄에 안 선다(실측 360px). */
  .world .league-donut { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: var(--sp-3); }
  .world .league-donut-legend { width: 100%; }
}
/* 카드 머리의 수익률 — 순위표와 **같은 수**다. 카드가 이미 순위 차례라 몇 번째인지는
   보이는데 얼마나 앞서는지는 위로 올라가야 알았다. 이름 바로 뒤, 종목 수 앞에 선다. */
.world .league-slip-return {
  flex: 0 0 auto; margin-left: auto;
  font: 700 var(--fs-sm)/1 var(--num); font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.world .league-slip-return.up { color: var(--up); }
.world .league-slip-return.down { color: var(--down); }
/* 수익률이 오른쪽 끝을 가져가므로 종목·현금 줄은 그 아래 제 줄로 내려간다. */
.world .league-slip > header { flex-wrap: wrap; }
.world .league-slip > header .league-meta { flex: 1 0 100%; margin-top: 2px; }

/* The grid: name and weight side by side, two columns when the card allows. */
.world .league-holdings.league-holdings-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 2px 18px; margin-top: 4px;
}
.world .league-holdings-grid li {
  grid-template-columns: minmax(0, 1fr) auto; column-gap: 10px; min-height: 28px;
  border-bottom: 1px solid var(--line);
}
.world .league-holdings-grid .league-hname { color: var(--ink); font-weight: 600; }
.world .league-holdings-grid .league-hw { font-size: 13px; }
.world .league-slip h3 { font-size: 16px; }
.world .league-slip .league-note { margin-top: 12px; }
/* Grouped ledger: one <details> per participant. */
.world .league-fills-caption { margin: 0; padding: 12px var(--panel-inset) 4px; color: var(--ink-3); font-size: 12px; line-height: 1.5; }
.world .league-fill-group { border-top: 1px solid var(--line); }
.world .league-fill-group > summary {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto 16px; align-items: center; gap: 12px;
  min-height: 48px; padding: 8px var(--panel-inset); cursor: pointer; list-style: none;
}
.world .league-fill-group > summary::-webkit-details-marker { display: none; }
.world .league-fill-group > summary::before {
  content: ""; grid-column: 4; grid-row: 1; justify-self: end; width: 8px; height: 8px; margin: 0 4px 3px 0;
  border-right: 2px solid var(--ink-3); border-bottom: 2px solid var(--ink-3); transform: rotate(45deg);
  transition: transform .18s;
}
.world .league-fill-group[open] > summary::before { transform: rotate(-135deg); }
.world .league-fill-group > summary:hover { background: var(--water-1); }
.world .league-fill-who { display: inline-flex; align-items: center; gap: 8px; min-width: 0; font-weight: 650; color: var(--ink); }
.world .league-fill-who small { color: var(--ink-3); font-weight: 500; }
.world .league-fill-sum { color: var(--ink-3); font: 500 12px/1.2 var(--num); font-variant-numeric: tabular-nums; white-space: nowrap; }
.world .league-fill-count { grid-column: 3; grid-row: 1; display: inline-flex; align-items: center; font: 600 12px/1 var(--num); color: var(--ink-2); }
.world .league-fill-group .table-scroll { border-top: 1px solid var(--line); }
.world .league-fill-group .league-fills th.league-stock { text-align: left; font-weight: 500; }
@media (max-width: 560px) {
  .world .league-fill-group > summary { grid-template-columns: minmax(0, 1fr) auto 16px; }
  .world .league-fill-sum { grid-column: 1; grid-row: 2; }
  .world .league-fill-count { grid-column: 2; grid-row: 1 / span 2; }
  .world .league-fill-group > summary::before { grid-column: 3; grid-row: 1 / span 2; }
}
/* /kr follow-ups from the same round */
.world #kridx-body { column-gap: var(--sp-4); }   /* the sector table needs 5px more than the wide gap left it */
.world .krbd-bar { max-width: 96px; }

/* 오늘의 한 마디 is words in the flow, not a card (console.css keeps it transparent);
 * the surface shadow every .pulse-side carries drew a ghost box around it, and the
 * heatmap heading sat 12px under it. No shadow, and the section keeps the page's gap. */
.world #home-quote { background: transparent; box-shadow: none; border: 0; }
.page-landing #constituent-heatmap { margin-top: 44px; }

/* --- Operator round 3, 2026-09-16 ------------------------------------------ */
/* Calendar: every week the same height. Content-sized rows made the weeks with
 * events 122px and the others 70 ("칸이 왜 이렇게 일정하지 않아"); a chip takes one
 * line and a cell shows three at most — the week list beside it carries the rest. */
.world .cal-grid { grid-auto-rows: 1fr; }
/* **폰은 칸 안에 일정을 적지 않는다.** 390px에서 한 칸은 48px이라 "미국 GDP"가
   "미..."로 잘렸고, 잘린 글자 아래에 같은 일정의 점이 또 찍혔다(운영자 제보
   2026-09-17). console.css가 이미 폰에서 칩을 감추고 점 + 아래 일정 목록으로
   답하고 있었는데, 이 줄이 폭을 가리지 않아 그것을 도로 켰다. 넓은 화면에만
   건다 — 거기서는 칸이 한 줄을 담을 만큼 넓다. */
@media (min-width: 761px) {
  .world .cal-cell { min-height: 92px; }
  .world .cal-cell .cal-chip { display: block; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; overflow-wrap: normal; }  /* a flex chip clipped its text with no ellipsis */
  .world .cal-cell .cal-chip:nth-of-type(n+4) { display: none; }
}
/* 지난 시장 요약 uses the card's whole width and height ("글이 너무 한쪽에 쏠려서
 * 몰려드는 느낌"): the lead figure leads, the sentence is not indented under it,
 * and the four market figures are a two-column table that sits on the bottom edge. */
.world #market-mood .mood-market-one { display: flex; flex-direction: column; min-height: 100%; }
.world #market-mood .mood-lead-pct { font-size: 30px; line-height: 1.1; }
.world #market-mood .mood-points { padding-left: 0; margin-top: 12px; font-size: 15px; line-height: 1.7; color: var(--ink); }
.world #market-mood .pulse-chips {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 20px; margin-top: auto; padding-top: 16px; overflow: visible;
}
.world #market-mood .pulse-chip {
  display: flex; align-items: baseline; gap: 6px; min-width: 0;
  padding: 8px 0; border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  background: transparent; font-size: 12.5px; color: var(--ink-2); white-space: nowrap;
}
.world #market-mood .pulse-chip > :first-child { color: var(--ink-3); }
.world #market-mood .pulse-chip .pulse-pct { margin-left: auto; font-size: 13px; }
.world #market-mood .pulse-chip > .lang-ko { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.world #market-mood .pulse-chips { gap: 0 26px; }
/* The row is named by the instrument (EWY, S&P 500…), so the 국내/미국 prefix only
 * cost the label its width ("EWY (한…"). */
.world #market-mood .pulse-chip > .pulse-chip-market { display: none; }
.world #market-mood .pulse-chip > .lang-ko { font-size: 12px; color: var(--ink-2); }
.world #market-mood .mood-line { display: inline; white-space: normal; }  /* a nowrap span per sentence broke lines at sentence ends */
/* The sentence fills its lines; balanced wrapping left a fifth of every line empty. */
.world #market-mood .mood-points > li, .world #market-mood .mood-sentence, .world #market-mood .mood-points .lang-ko, .world #market-mood .mood-line { text-wrap: wrap; }  /* "normal" is not a text-wrap value; the declaration was dropped */
/* 자세히 보기 starts where the cards start (it began 31px in on /kr and /us). */
.world .disclose > summary { padding-left: 0; padding-right: 0; }
/* …but a disclosure that lives inside a panel (the calendar's 전체 일정) keeps the
 * panel's inset, or its text sits on the panel edge. */
.world .panel .disclose > summary { padding-left: var(--sp-3); padding-right: var(--sp-3); }

/* Section text sits on the card's text column. A panel's words start one inset
 * inside its edge, so a heading flush with the panel edge sat 16px left of the
 * words below it. The heading, the notes and the filter row above a panel start
 * on that column too. (The first try bled the cards outward instead; the operator
 * read that as cards poking out of the page — "이럴거면 전이 나아".) The home keeps
 * its own grammar: the state rule beside the heading and cards that are the page. */
/* 폭을 가리지 않는다. 데스크톱만 고쳐 두었더니 폰에서는 제목이 판 안 글보다
 * 12px 왼쪽에 섰다(실측 2026-09-17, /kr /us /crypto 전 섹션). `--panel-inset`이
 * 폰에서 12px이라 값은 저절로 따라간다. */
.world:not(.page-landing) .console-section > :is(header, .term-hints, .sec-note, .ev-model, .feed-filter, .segmented, .krl-basis, .easy-only, p) {
  padding-left: var(--panel-inset);
}
.world:not(.page-landing) .console-section > .disclose > summary { padding-left: var(--panel-inset); }
@media (min-width: 761px) {
  /* The heading's text on the panel's column alone read as the panels poking out
   * to the left (operator, 2026-09-16: "박스들이 더 왼쪽으로 튀어나온 느낌 — 홈은
   * 괜찮은데"). The home is fine because its heading carries a 3px rule on the
   * panel's edge: the rule marks the edge, the text sits on the column. The
   * other pages get the same rule, toned by the section's live-data state. */
  /* **룰은 제목 글자만큼만 선다.** 헤더에 걸어 두었더니 헤더가 오른쪽 메타까지
     안는 상자라 막대가 글자의 두 배 높이였다(운영자 2026-09-17 "순위 여기도
     옆에 세로 바랑 align 안 맞고"). 제목에 걸면 줄 수가 몇이든 글자를 따라간다.
     제목은 `--panel-inset`만큼 들어와 있으므로 그만큼 왼쪽에 세우면 판 가장자리다. */
  .world:not(.page-landing) .console-section > header > :is(h1, h2) { position: relative; }
  .world:not(.page-landing) .console-section > header > :is(h1, h2)::before {
    content: "";
    position: absolute;
    top: .16em;
    bottom: .16em;
    left: calc(var(--panel-inset) * -1);
    width: 3px;
    border-radius: 999px;
    background: var(--line-strong);
  }
  .world:not(.page-landing) .console-section:has(.state-block[data-state="ready"]) > header > :is(h1, h2)::before {
    background: linear-gradient(180deg, var(--chart), var(--current-2));
  }
  .world:not(.page-landing) .console-section:has(.state-block[data-state="waiting"], .state-block[data-state="loading"]) > header > :is(h1, h2)::before {
    background: var(--amber);
  }
  .world:not(.page-landing) .console-section:has(.state-block[data-state="error"]) > header > :is(h1, h2)::before {
    background: var(--red);
  }
  /* The league's boxed introduction already insets everything by its own padding
   * and its method panel has no inner inset: the title stays on the box's column. */
  .world .league-page .console-section:first-child > header { padding-left: 0; }
  .world .league-page .console-section:first-child > header > :is(h1, h2)::before { content: none; }
}

/* --- Operator round 5, 2026-09-16 ------------------------------------------ */
/* English follows the Western convention: green up, red down — the locale's own
 * default, as production already did. */
html[data-lang="en"] .world { --up: #1c8f57; --down: #c92a3e; }
html[data-lang="en"][data-theme="dark"] .world { --up: #3ecf8e; --down: #ff6b7a; }
/* The English placeholder ("Search stocks or crypto") is wider than the Korean one.
 *
 * **Desktop only.** Unscoped, this leaked into the phone layout, where the field
 * is absolutely positioned against the right edge of a 44px button: a 236px
 * width put its left edge — and the magnifier inside it — on top of the brand,
 * so the masthead read "QMulmit" and the search was unreachable (operator,
 * 2026-09-17, English mode at 390px). The phone keeps its own 44px token. */
@media (min-width: 761px) {
  html[data-lang="en"] .site-search { --search-rest: 236px; --search-open: 284px; }
}
/* Weekend link under the Korea overnight cards (shown Sat/Sun by monitor.js). */
.world .krov-weekend { margin: 8px 0 0; font-size: 13px; }
.world .krov-weekend a { color: var(--accent); text-decoration: none; font-weight: 600; }

/* ============================================================================
 * One vocabulary — 같은 뜻은 같은 모양 (2026-09-17)
 *
 * 화면이 여러 시기에 자라면서 같은 것이 이름만 다르게 여러 벌이 됐다. 실측으로
 * 센 것(1440px · light · 16개 화면):
 *
 *   카드 열두 계열 — 모서리 다섯 값(0·10·15·16·18) · 여백 여덟 값 · 그림자 두 값
 *   표 아홉 계열   — 머리 배경 두 값 · 머리 대문자화 두 값 · 자간 네 값 ·
 *                    칸 글자 두 값(12·13) · 칸 여백 네 값 · 머리 9.5px(하한 미달)
 *   칩 아홉 계열   — 모서리 네 값 · 여백 여섯 값 · 글자 네 값 · 굵기 네 값
 *   탭 다섯 계열   — 여백 다섯 값 · 글자 세 값 · 굵기 세 값
 *
 * **이름을 합치지 않는다.** 클래스 개명은 아흔한 개 프론트 테스트와 서버 렌더러가
 * 걸린 작업이고, 지켜야 하는 것은 이름이 아니라 **보이는 결과**다. 그래서 여기서
 * 한 벌의 값을 계열 전체에 건다. 새 계열이 생기면 이 목록에 이름만 더한다.
 *
 * 값은 전부 토큰이다 — 이 블록에 리터럴이 있으면 그건 버그다.
 * ========================================================================== */

/* --- 표: 머리는 조용한 물빛 띠, 칸은 본문 한 단 아래 -------------------------
 * 대문자화는 뺀다. 한국어에는 대문자가 없어서 같은 표의 한글 머리는 그대로이고
 * 영문 머리만 소리를 질렀다 — 두 언어를 쓰는 제품에서 위계가 언어에 따라
 * 달라지는 셈이다. 자간도 그래서 normal로 돌아온다(대문자에 붙이던 값이었다). */
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table, .league-table, .kridx-table, .bio-table) th {
  background: var(--water-1);
  font-size: var(--fs-xs);
  font-weight: 650;
  text-transform: none;
  letter-spacing: normal;
}
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table, .league-table, .kridx-table, .bio-table) :is(th, td) {
  padding: var(--sp-2) var(--sp-3);
}
/* 칸 글자는 12px이 아니라 13px이다. 12px은 **라벨** 하한선이고, 표의 칸은
 * 읽는 본문이다 — 다섯 계열이 라벨 크기로 값을 적고 있었다. */
.world :is(.accessible-table, .stock-table, .asset-table, .coin-table,
  .score-table, .crypto-table, .league-table, .kridx-table, .bio-table) td {
  font-size: var(--fs-sm);
}

/* --- 카드: 한 면, 한 모서리, 한 그림자 ---------------------------------------
 * `.zone-link-card`와 `.stock-kpi`는 여기서 뺀다 — 판 **안의 줄**이지 카드가
 * 아니다(테두리도 그림자도 없는 것이 옳다). 카드 안의 카드는 이 저장소가
 * 이미 한 번 되돌린 모양이다. */
.world :is(.metric-card, .kro-card, .cliq-card, .crypto-card, .cboard-card,
  .tile, .deck-tile, .asset-kpi, .coin-kpi) {
  border: 0;
  border-radius: var(--r-xl);
  background: var(--panel);
  box-shadow: var(--surface-shadow);
  padding: var(--sp-4);
  font-size: var(--fs-md);
}
/* 셰브런이 앉는 타일만 오른쪽에 자리를 비운다. */
.world .deck-tile { padding-right: 34px; }
.world :is(.metric-card, .kro-card, .cliq-card, .crypto-card, .cboard-card, .tile):hover {
  border: 0;
  box-shadow: var(--surface-shadow-lift);
}

/* --- 칩: 알약 하나 ------------------------------------------------------------
 * `.pulse-chip`은 이름만 칩이고 실제로는 요약 카드 바닥의 표 한 줄이라 뺀다. */
.world :is(.nav-chip, .bio-chip, .cal-chip, .hint-chip, .league-chip,
  .resume-chip, .source-pill, .glossary-chip) {
  min-height: var(--tap-min);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--panel);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 650;
  letter-spacing: -.01em;
}
.world :is(.nav-chip, .bio-chip, .cal-chip, .hint-chip, .league-chip,
  .resume-chip, .glossary-chip):hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* --- 고르는 것은 세 층이다 ----------------------------------------------------
 * 처음에는 다섯 계열을 한 모양으로 끌어모으려 했는데, 재 보니 그게 틀렸다.
 * 세 층은 서로 다른 일을 한다:
 *
 *   1. **밑줄 탭** — 화면 안의 갈래를 가른다(`.view-tab`, `.stock-tab`,
 *      `.chart-tabs button`). 지금 자리는 색이 아니라 2px 밑줄과 굵기로 말한다.
 *   2. **거르개 알약** — 같은 목록을 좁힌다(`.segmented button`, `.scr-tab`,
 *      `.news-filter button`, `.bio-filter button`). 위 347줄이 이미 한 벌로
 *      묶어 두었고, `.scr-tab`을 밑줄로 끌어오려던 시도는 되돌렸다.
 *   3. **이동 칩** — 다른 자리로 보낸다(`.stock-subnav a`, `.nav-chip`).
 *
 * 층마다 안에서 일관되면 된다. 셋을 한 모양으로 만들면 무엇이 갈래를 바꾸고
 * 무엇이 목록을 좁히는지 눈으로 구별할 수 없다.
 *
 * 함정 기록: `.world :is(.scr-tab, .segmented button, …)`는 `:is()` 안의 가장
 * 구체적인 인자(`.segmented button`)를 따라 (0,2,1)이 된다. 뒤에 쓴
 * `.world .scr-tab`(0,2,0)이 **늦게 실려도 진다.** 이 저장소에서 두 번째다. */
.world :is(.view-tab, .stock-tab, .chart-tabs button:not(.tv-fs-button)) {
  font-size: var(--fs-sm);
  font-weight: 650;
}
/* 떨어져 선 거르개 알약 셋은 모서리·테두리·크기가 이미 같았는데 **굵기만**
 * 400·550·650으로 갈려 있었다(실측 2026-09-17). 같은 일을 하는 컨트롤이
 * 화면마다 다른 무게로 서면 어느 것이 지금 켜져 있는지가 흐려진다.
 * `.segmented`는 여기 넣지 않는다 — 그것은 칸이 붙은 한 덩이 컨트롤이다. */
.world :is(.scr-tab, .news-filter button, .bio-filter button) {
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--fs-xs);
  font-weight: 650;
}

/* --- 불러오는 자리는 값이 올 자리만큼 잡는다 --------------------------------
 * 실측(2026-09-17, 브라우저 layout-shift): `/bio` CLS 2.99 · `/kr` 1.25 ·
 * 홈 1.57. 큰 원인 하나 — 상태 블록이 43px인데 그 자리에 오는 판은 200~600px라,
 * lane이 답할 때마다 아래 화면이 통째로 밀린다.
 *
 * `html.booting`은 첫 라운드가 끝나면 떨어지는데 느린 lane은 그 뒤에 온다.
 * 그래서 기다리는 **상태 자체**를 창으로 쓴다 — 기다리는 동안만 자리를 잡고,
 * 값이 오면 그 자리를 값이 받는다. lane이 꺼진 자리(off·failed)는 자리를
 * 잡지 않는다: 영영 안 올 값을 위해 화면을 비워 두지 않는다. */
/* --- 기다리는 자리는 값이 올 자리만큼 잡는다 (섹션마다 제 크기로) ----------
 * 한 값(260px)으로 전부 잡아 봤더니 짧은 섹션에는 남고 긴 섹션에는 모자랐다.
 * 섹션마다 값이 다 온 뒤의 **실제 높이**를 재서 그만큼 잡는다. 아래 목록이
 * 그 실측값이고, 120~620px으로 자른다 — 더 크게 잡으면 lane이 꺼졌을 때의
 * 접힘이 그만큼 커져 오히려 밀림이 는다.
 *
 * 이름이 목록에 없는 섹션은 기본값을 받는다. */
.world .console-section .state-block[data-state="waiting"],
.world .console-section .state-block[data-state="collecting"] {
  min-height: 200px;
  align-content: center;
}

/* --- 기다리는 자리는 **그 섹션이 실제로 차지할 만큼** 잡는다 ---------------
 * 한 값으로 전부 잡던 때(260px)는 짧은 섹션에 남고 긴 섹션에 모자라 밀림이
 * 남았다. 아래 값은 값이 다 온 뒤의 실제 높이를 재서 적은 것이다
 * (scripts 없이 재현하려면 `impeccable/`의 기록을 보라, 2026-09-17 실측).
 * 120~620px으로 자른다 — 더 크게 잡으면 lane이 꺼졌을 때의 접힘이 그만큼
 * 커져서 오히려 밀림이 는다. */
/* 넓은 화면은 **제 값으로** 잡는다 (2026-09-17).
 *
 * 폰에서 잰 높이를 폭을 가리지 않고 걸어 두었더니 데스크톱에서 거꾸로 밀렸다 —
 * 넓은 칸은 줄이 덜 접혀 실제 높이가 낮은데 620px을 잡고 있다가 값이 오면 그만큼
 * **위로** 당겨졌다(실측: 홈의 미국 야간 745 → 360). 덜 잡는 것과 더 잡는 것은
 * 같은 부호의 밀림이 아니다 — 더 잡으면 읽던 자리가 위로 사라진다.
 *
 * 같은 섹션이 두 페이지에 서면 높이가 다르다(`#us-overnight`은 홈에서 360,
 * `/us`에서 그보다 크다). 홈만 제 클래스를 갖고 있으므로 홈 값은 따로 적는다.
 * 1440px 실측, 120~620px으로 자른다. */
@media (min-width: 761px) {
  .world #bio-adcomm .state-block[data-state="waiting"],
  .world #bio-adcomm .state-block[data-state="collecting"] { min-height: 731px; }
  .world #bio-catalysts .state-block[data-state="waiting"],
  .world #bio-catalysts .state-block[data-state="collecting"] { min-height: 900px; }
  .world #bio-fda .state-block[data-state="waiting"],
  .world #bio-fda .state-block[data-state="collecting"] { min-height: 900px; }
  .world #bio-mfds .state-block[data-state="waiting"],
  .world #bio-mfds .state-block[data-state="collecting"] { min-height: 900px; }
  .world #bio-outcomes .state-block[data-state="waiting"],
  .world #bio-outcomes .state-block[data-state="collecting"] { min-height: 900px; }
  .world #bio-trials-global .state-block[data-state="waiting"],
  .world #bio-trials-global .state-block[data-state="collecting"] { min-height: 900px; }
  .world #bio-trials-kr .state-block[data-state="waiting"],
  .world #bio-trials-kr .state-block[data-state="collecting"] { min-height: 837px; }
  .world #commodities .state-block[data-state="waiting"],
  .world #commodities .state-block[data-state="collecting"] { min-height: 432px; }
  .world #crypto-board .state-block[data-state="waiting"],
  .world #crypto-board .state-block[data-state="collecting"] { min-height: 900px; }
  .world #crypto-derivatives .state-block[data-state="waiting"],
  .world #crypto-derivatives .state-block[data-state="collecting"] { min-height: 569px; }
  .world #crypto-gas .state-block[data-state="waiting"],
  .world #crypto-gas .state-block[data-state="collecting"] { min-height: 296px; }
  .world #crypto-liq .state-block[data-state="waiting"],
  .world #crypto-liq .state-block[data-state="collecting"] { min-height: 296px; }
  .world #crypto-news .state-block[data-state="waiting"],
  .world #crypto-news .state-block[data-state="collecting"] { min-height: 144px; }
  .world #crypto-regime .state-block[data-state="waiting"],
  .world #crypto-regime .state-block[data-state="collecting"] { min-height: 266px; }
  .world #crypto-sentiment .state-block[data-state="waiting"],
  .world #crypto-sentiment .state-block[data-state="collecting"] { min-height: 765px; }
  .world #crypto-structure .state-block[data-state="waiting"],
  .world #crypto-structure .state-block[data-state="collecting"] { min-height: 585px; }
  .world #crypto-tape .state-block[data-state="waiting"],
  .world #crypto-tape .state-block[data-state="collecting"] { min-height: 900px; }
  .world #crypto-volatility .state-block[data-state="waiting"],
  .world #crypto-volatility .state-block[data-state="collecting"] { min-height: 486px; }
  .world #datacenter-board .state-block[data-state="waiting"],
  .world #datacenter-board .state-block[data-state="collecting"] { min-height: 599px; }
  .world #datacenter-capex .state-block[data-state="waiting"],
  .world #datacenter-capex .state-block[data-state="collecting"] { min-height: 636px; }
  .world #fed-digest .state-block[data-state="waiting"],
  .world #fed-digest .state-block[data-state="collecting"] { min-height: 900px; }
  .world #fomc-dots .state-block[data-state="waiting"],
  .world #fomc-dots .state-block[data-state="collecting"] { min-height: 430px; }
  .world #fomc-statement .state-block[data-state="waiting"],
  .world #fomc-statement .state-block[data-state="collecting"] { min-height: 568px; }
  .world #index-returns .state-block[data-state="waiting"],
  .world #index-returns .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-breadth .state-block[data-state="waiting"],
  .world #kr-breadth .state-block[data-state="collecting"] { min-height: 342px; }
  .world #kr-buyback .state-block[data-state="waiting"],
  .world #kr-buyback .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-etf .state-block[data-state="waiting"],
  .world #kr-etf .state-block[data-state="collecting"] { min-height: 888px; }
  .world #kr-events .state-block[data-state="waiting"],
  .world #kr-events .state-block[data-state="collecting"] { min-height: 639px; }
  .world #kr-holdings .state-block[data-state="waiting"],
  .world #kr-holdings .state-block[data-state="collecting"] { min-height: 635px; }
  .world #kr-indices .state-block[data-state="waiting"],
  .world #kr-indices .state-block[data-state="collecting"] { min-height: 629px; }
  .world #kr-industry .state-block[data-state="waiting"],
  .world #kr-industry .state-block[data-state="collecting"] { min-height: 801px; }
  .world #kr-insider .state-block[data-state="waiting"],
  .world #kr-insider .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-ipo .state-block[data-state="waiting"],
  .world #kr-ipo .state-block[data-state="collecting"] { min-height: 673px; }
  .world #kr-leaders .state-block[data-state="waiting"],
  .world #kr-leaders .state-block[data-state="collecting"] { min-height: 575px; }
  .world #kr-macro-host .state-block[data-state="waiting"],
  .world #kr-macro-host .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-options .state-block[data-state="waiting"],
  .world #kr-options .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-overnight .state-block[data-state="waiting"],
  .world #kr-overnight .state-block[data-state="collecting"] { min-height: 440px; }
  .world #kr-pension .state-block[data-state="waiting"],
  .world #kr-pension .state-block[data-state="collecting"] { min-height: 684px; }
  .world #kr-pension-portfolio .state-block[data-state="waiting"],
  .world #kr-pension-portfolio .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-region .state-block[data-state="waiting"],
  .world #kr-region .state-block[data-state="collecting"] { min-height: 804px; }
  .world #kr-search-interest .state-block[data-state="waiting"],
  .world #kr-search-interest .state-block[data-state="collecting"] { min-height: 900px; }
  .world #kr-top-grades .state-block[data-state="waiting"],
  .world #kr-top-grades .state-block[data-state="collecting"] { min-height: 900px; }
  .world #mkt-commodity .state-block[data-state="waiting"],
  .world #mkt-commodity .state-block[data-state="collecting"] { min-height: 338px; }
  .world #mkt-etf .state-block[data-state="waiting"],
  .world #mkt-etf .state-block[data-state="collecting"] { min-height: 413px; }
  .world #mkt-foreign .state-block[data-state="waiting"],
  .world #mkt-foreign .state-block[data-state="collecting"] { min-height: 451px; }
  .world #mkt-fx .state-block[data-state="waiting"],
  .world #mkt-fx .state-block[data-state="collecting"] { min-height: 149px; }
  .world #mkt-index .state-block[data-state="waiting"],
  .world #mkt-index .state-block[data-state="collecting"] { min-height: 451px; }
  .world #mkt-other .state-block[data-state="waiting"],
  .world #mkt-other .state-block[data-state="collecting"] { min-height: 186px; }
  .world #mkt-private .state-block[data-state="waiting"],
  .world #mkt-private .state-block[data-state="collecting"] { min-height: 753px; }
  .world #mkt-us_stock .state-block[data-state="waiting"],
  .world #mkt-us_stock .state-block[data-state="collecting"] { min-height: 900px; }
  .world #rate-expectations .state-block[data-state="waiting"],
  .world #rate-expectations .state-block[data-state="collecting"] { min-height: 489px; }
  .world #semi-financials .state-block[data-state="waiting"],
  .world #semi-financials .state-block[data-state="collecting"] { min-height: 900px; }
  .world #semi-funds .state-block[data-state="waiting"],
  .world #semi-funds .state-block[data-state="collecting"] { min-height: 223px; }
  .world #semi-memory .state-block[data-state="waiting"],
  .world #semi-memory .state-block[data-state="collecting"] { min-height: 816px; }
  .world #semi-price .state-block[data-state="waiting"],
  .world #semi-price .state-block[data-state="collecting"] { min-height: 607px; }
  .world #sentiment-index .state-block[data-state="waiting"],
  .world #sentiment-index .state-block[data-state="collecting"] { min-height: 727px; }
  .world #stress-index .state-block[data-state="waiting"],
  .world #stress-index .state-block[data-state="collecting"] { min-height: 566px; }
  .world #us-board .state-block[data-state="waiting"],
  .world #us-board .state-block[data-state="collecting"] { min-height: 411px; }
  .world #us-events .state-block[data-state="waiting"],
  .world #us-events .state-block[data-state="collecting"] { min-height: 900px; }
  .world #us-ipo .state-block[data-state="waiting"],
  .world #us-ipo .state-block[data-state="collecting"] { min-height: 699px; }
  .world #us-managers .state-block[data-state="waiting"],
  .world #us-managers .state-block[data-state="collecting"] { min-height: 900px; }
  .world #us-overnight .state-block[data-state="waiting"],
  .world #us-overnight .state-block[data-state="collecting"] { min-height: 883px; }
  .world #us-ptr .state-block[data-state="waiting"],
  .world #us-ptr .state-block[data-state="collecting"] { min-height: 900px; }
  .world.page-landing #constituent-heatmap .state-block[data-state="waiting"],
  .world.page-landing #constituent-heatmap .state-block[data-state="collecting"] { min-height: 620px; }
  .world.page-landing #econ-calendar .state-block[data-state="waiting"],
  .world.page-landing #econ-calendar .state-block[data-state="collecting"] { min-height: 620px; }
  .world.page-landing #news-videos .state-block[data-state="waiting"],
  .world.page-landing #news-videos .state-block[data-state="collecting"] { min-height: 285px; }
  .world.page-landing #signal-feed .state-block[data-state="waiting"],
  .world.page-landing #signal-feed .state-block[data-state="collecting"] { min-height: 620px; }
  .world.page-landing #us-overnight .state-block[data-state="waiting"],
  .world.page-landing #us-overnight .state-block[data-state="collecting"] { min-height: 268px; }
  .world.page-landing #zones .state-block[data-state="waiting"],
  .world.page-landing #zones .state-block[data-state="collecting"] { min-height: 448px; }

  /* **비어 있는 섹션에도 자리를 잡는다.** 홈의 두 섹션은 마크업에 서 있지만 몸이
     전부 `hidden`이라 첫 페인트에서 높이가 0이고, 값이 오는 순간 700·742px이
     통째로 끼어들어 아래를 밀었다(실측 1440px). 상태 블록에 건 예약은 그
     블록마저 숨어 있어 소용이 없었다 — 그래서 **섹션 자신에게** 건다.
     몸 중 하나라도 서면 조건이 풀리고 자리는 값이 받는다. */
  .world.page-landing #econ-calendar:not(:has(> :not(header):not([hidden]))) { min-height: 700px; }
  .world.page-landing #signal-feed:not(:has(> :not(header):not([hidden]))) { min-height: 742px; }
}
@media (max-width: 760px) {
  .world #bio-adcomm .state-block[data-state="waiting"],
  .world #bio-adcomm .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-catalysts .state-block[data-state="waiting"],
  .world #bio-catalysts .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-fda .state-block[data-state="waiting"],
  .world #bio-fda .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-mfds .state-block[data-state="waiting"],
  .world #bio-mfds .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-outcomes .state-block[data-state="waiting"],
  .world #bio-outcomes .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-trials-global .state-block[data-state="waiting"],
  .world #bio-trials-global .state-block[data-state="collecting"] { min-height: 620px; }
  .world #bio-trials-kr .state-block[data-state="waiting"],
  .world #bio-trials-kr .state-block[data-state="collecting"] { min-height: 620px; }
  .world #commodities .state-block[data-state="waiting"],
  .world #commodities .state-block[data-state="collecting"] { min-height: 620px; }
  .world #constituent-heatmap .state-block[data-state="waiting"],
  .world #constituent-heatmap .state-block[data-state="collecting"] { min-height: 584px; }
  .world #crypto-board .state-block[data-state="waiting"],
  .world #crypto-board .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-derivatives .state-block[data-state="waiting"],
  .world #crypto-derivatives .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-gas .state-block[data-state="waiting"],
  .world #crypto-gas .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-kimchi .state-block[data-state="waiting"],
  .world #crypto-kimchi .state-block[data-state="collecting"] { min-height: 462px; }
  .world #crypto-liq .state-block[data-state="waiting"],
  .world #crypto-liq .state-block[data-state="collecting"] { min-height: 559px; }
  .world #crypto-news .state-block[data-state="waiting"],
  .world #crypto-news .state-block[data-state="collecting"] { min-height: 227px; }
  .world #crypto-regime .state-block[data-state="waiting"],
  .world #crypto-regime .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-sentiment .state-block[data-state="waiting"],
  .world #crypto-sentiment .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-structure .state-block[data-state="waiting"],
  .world #crypto-structure .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-tape .state-block[data-state="waiting"],
  .world #crypto-tape .state-block[data-state="collecting"] { min-height: 620px; }
  .world #crypto-volatility .state-block[data-state="waiting"],
  .world #crypto-volatility .state-block[data-state="collecting"] { min-height: 620px; }
  .world #datacenter-board .state-block[data-state="waiting"],
  .world #datacenter-board .state-block[data-state="collecting"] { min-height: 620px; }
  .world #datacenter-capex .state-block[data-state="waiting"],
  .world #datacenter-capex .state-block[data-state="collecting"] { min-height: 208px; }
  .world #econ-calendar .state-block[data-state="waiting"],
  .world #econ-calendar .state-block[data-state="collecting"] { min-height: 620px; }
  .world #fed-digest .state-block[data-state="waiting"],
  .world #fed-digest .state-block[data-state="collecting"] { min-height: 620px; }
  .world #fomc-dots .state-block[data-state="waiting"],
  .world #fomc-dots .state-block[data-state="collecting"] { min-height: 479px; }
  .world #fomc-statement .state-block[data-state="waiting"],
  .world #fomc-statement .state-block[data-state="collecting"] { min-height: 620px; }
  .world #index-returns .state-block[data-state="waiting"],
  .world #index-returns .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-breadth .state-block[data-state="waiting"],
  .world #kr-breadth .state-block[data-state="collecting"] { min-height: 366px; }
  .world #kr-buyback .state-block[data-state="waiting"],
  .world #kr-buyback .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-etf .state-block[data-state="waiting"],
  .world #kr-etf .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-events .state-block[data-state="waiting"],
  .world #kr-events .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-holdings .state-block[data-state="waiting"],
  .world #kr-holdings .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-indices .state-block[data-state="waiting"],
  .world #kr-indices .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-industry .state-block[data-state="waiting"],
  .world #kr-industry .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-insider .state-block[data-state="waiting"],
  .world #kr-insider .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-ipo .state-block[data-state="waiting"],
  .world #kr-ipo .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-leaders .state-block[data-state="waiting"],
  .world #kr-leaders .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-macro-host .state-block[data-state="waiting"],
  .world #kr-macro-host .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-options .state-block[data-state="waiting"],
  .world #kr-options .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-overnight .state-block[data-state="waiting"],
  .world #kr-overnight .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-pension .state-block[data-state="waiting"],
  .world #kr-pension .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-pension-portfolio .state-block[data-state="waiting"],
  .world #kr-pension-portfolio .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-region .state-block[data-state="waiting"],
  .world #kr-region .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-search-interest .state-block[data-state="waiting"],
  .world #kr-search-interest .state-block[data-state="collecting"] { min-height: 620px; }
  .world #kr-top-grades .state-block[data-state="waiting"],
  .world #kr-top-grades .state-block[data-state="collecting"] { min-height: 620px; }
  .world #news-videos .state-block[data-state="waiting"],
  .world #news-videos .state-block[data-state="collecting"] { min-height: 620px; }
  .world #rate-expectations .state-block[data-state="waiting"],
  .world #rate-expectations .state-block[data-state="collecting"] { min-height: 558px; }
  .world #semi-financials .state-block[data-state="waiting"],
  .world #semi-financials .state-block[data-state="collecting"] { min-height: 620px; }
  .world #semi-funds .state-block[data-state="waiting"],
  .world #semi-funds .state-block[data-state="collecting"] { min-height: 620px; }
  .world #semi-memory .state-block[data-state="waiting"],
  .world #semi-memory .state-block[data-state="collecting"] { min-height: 620px; }
  .world #semi-price .state-block[data-state="waiting"],
  .world #semi-price .state-block[data-state="collecting"] { min-height: 620px; }
  .world #sentiment-index .state-block[data-state="waiting"],
  .world #sentiment-index .state-block[data-state="collecting"] { min-height: 620px; }
  .world #signal-feed .state-block[data-state="waiting"],
  .world #signal-feed .state-block[data-state="collecting"] { min-height: 620px; }
  .world #stress-index .state-block[data-state="waiting"],
  .world #stress-index .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-board .state-block[data-state="waiting"],
  .world #us-board .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-events .state-block[data-state="waiting"],
  .world #us-events .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-ipo .state-block[data-state="waiting"],
  .world #us-ipo .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-managers .state-block[data-state="waiting"],
  .world #us-managers .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-overnight .state-block[data-state="waiting"],
  .world #us-overnight .state-block[data-state="collecting"] { min-height: 620px; }
  .world #us-ptr .state-block[data-state="waiting"],
  .world #us-ptr .state-block[data-state="collecting"] { min-height: 620px; }
  .world #zones .state-block[data-state="waiting"],
  .world #zones .state-block[data-state="collecting"] { min-height: 620px; }
}

/* 빈 판은 정의상 **기다리는 판**이다 — 값이 없는 섹션은 통째로 접히는 것이
 * 이 저장소의 규칙이므로(fail-closed), 화면에 서 있는 빈 판은 아직 안 온 값의
 * 자리다. `/bio`처럼 상태 블록 없이 판만 두고 채우는 섹션이 여기 걸린다
 * (실측 2026-09-17: `#bio-catalysts` 0 → 388px). 값이 오면 제 높이를 갖는다. */
.world .console-section > .panel:empty,
.world .console-section > .bio-zone:empty { min-height: 200px; }

/* ── 호버 모션 ───────────────────────────────────────────────────────────────
 *
 * 운영자 2026-09-17: *"전체적으로 호버했을 때 모션이 딱딱하거나 없어."*
 *
 * 실측하니 `:hover` 규칙이 111개인데 `transition` 선언은 59개다. 특히 카드가
 * **드는** 자리(이 파일 143·191·2039행)는 전환이 하나도 없어 그림자와 -2px이
 * 한 프레임에 튄다 — 모션이 "딱딱한" 것이 아니라 아예 없는 것이다.
 *
 * 규칙 111개를 하나씩 고치는 대신 층을 하나 더 얹는다. 지켜야 할 것 셋:
 *
 * 1. **애니메이트할 속성을 이름으로 적는다.** `transition: all`이면 값이 도착
 *    할 때 폭·높이까지 따라 움직여 판이 밀린다(CLS). 색·면·테두리·그림자·
 *    투명도·`transform`만 적는다.
 * 2. **들어올 때는 빠르고 나갈 때는 길다.** 같은 시간이면 손을 뗀 뒤 화면이
 *    먼저 끝나 버려 뚝 끊기는 느낌이 남는다. 기본 상태에 긴 시간을 두고
 *    `:hover`에서 짧게 덮는다.
 * 3. **`@media (hover: hover)`로 감싼다.** 폰에서 탭한 뒤 면이 남아 방금 누른
 *    단추가 켜진 상태로 보이던 일(802행 주석)을 모션이 더 길게 끌지 않게.
 */
:root {
  --hover-in: 130ms;
  --hover-out: 260ms;
  --hover-ease: cubic-bezier(.2, .7, .3, 1);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* **두 규칙이 같은 속성 목록을 쓴다.**
   *
   * 처음에는 색만 바뀌는 것들과 드는 면을 갈라 적었다. 그랬더니 `a.tile`이 둘 다에
   * 걸리는데 색 쪽 규칙이 이겨 `transform`이 목록에서 빠졌다 — 그림자만 부드럽고
   * -2px은 그대로 튀었다(실측: 호버 60ms에 이미 끝값). `:is()`는 **가장 구체적인
   * 인자**의 특이도를 갖는다: 색 쪽에 든 `.segmented button`이 (0,2,1)을 만들어
   * (0,2,0)인 카드 규칙을 이겼다. 갈라 적는 한 다음 사람도 같은 자리에 빠진다.
   * 목록을 같게 두면 어느 쪽이 이기든 전부 움직인다.
   */
  .world :is(a, button, summary, label, [role="button"], [role="tab"], tbody tr,
    .chip, .pill, .segmented button, .ask-option, .view-tab, .jump-link,
    .tile, .metric-card, .kro-card, .cliq-card, .crypto-card,
    .cboard-card, .zone-link-card, .deck-tile) {
    transition:
      transform var(--hover-out) var(--hover-ease),
      color var(--hover-out) var(--hover-ease),
      background-color var(--hover-out) var(--hover-ease),
      border-color var(--hover-out) var(--hover-ease),
      box-shadow var(--hover-out) var(--hover-ease),
      opacity var(--hover-out) var(--hover-ease);
  }

  /* 손이 닿는 순간은 절반의 시간으로 — 반응은 즉각, 되돌아감은 여유롭게. */
  .world :is(a, button, summary, label, [role="button"], [role="tab"], tbody tr,
    .chip, .pill, .segmented button, .ask-option, .view-tab, .jump-link,
    .tile, .metric-card, .kro-card, .cliq-card, .crypto-card,
    .cboard-card, .zone-link-card, .deck-tile):hover {
    transition-duration: var(--hover-in);
  }

  /* 아이콘 단추는 면이 작아 색만으로는 눌린 줄 모른다. 아주 조금 커진다. */
  .world .icon-button {
    transition:
      color var(--hover-out) var(--hover-ease),
      background-color var(--hover-out) var(--hover-ease),
      transform var(--hover-out) var(--hover-ease);
  }
  .world .icon-button:hover { transition-duration: var(--hover-in); transform: scale(1.06); }
  .world .icon-button:active { transform: scale(.96); transition-duration: 80ms; }

  /* 누르는 순간의 되돌림 — 링크·카드도 손가락을 따라 아주 살짝 내려앉는다. */
  .world :is(a.tile, .zone-link-card):active { transform: translateY(0); transition-duration: 80ms; }

  /* **직접 조작에는 전환이 없다.**
   *
   * 위에서 `.tile`에 transform 전환을 주자 끌어서 순서를 바꾸는 조작이 망가졌다
   * (운영자 2026-09-18: *"드래그해서 위치 바꾸는 모션이 너무 예민한데? 예전엔
   * 괜찮았는데."*). 끌 때 `follow()`가 매 `pointermove`마다 인라인 transform을
   * 쓰는데, 전환이 걸려 있으면 그때마다 260ms 이징이 처음부터 다시 시작한다 —
   * 판이 손가락을 한참 뒤따라가고 지나쳐서 멈춘다(운영 실측: 160px 옮긴 직후
   * 판은 63px, 300ms 뒤 189px). 그동안 `reseat()`은 **포인터 자리**로 자리바꿈을
   * 판정하므로, 한 번 끄는 동안 여러 번 바뀐 것처럼 느껴진다.
   *
   * 끌기는 손가락이 곧 위치인 조작이라 사이에 낄 곡선이 없다. 자리바꿈의 미끄러짐은
   * `flipReorder`가 제 타이밍(260ms)으로 이미 갖고 있다 — 그건 전환이 아니라
   * `element.animate()`라서 여기 규칙과 다투지 않는다.
   *
   * **선택자 둘을 실측으로 골랐다.** 처음에는 `.world .tile-editing`과
   * `.world .my-board.is-dragging .tile`로 적었는데 둘 다 안 먹었다:
   *   · 앞의 것은 (0,2,0)이라 위 블랭킷 규칙을 못 이긴다. `:is()`는 **가장
   *     구체적인 인자**의 특이도를 갖고, 그 목록에 든 `.segmented button`이
   *     규칙 전체를 (0,2,1)로 올려 놓았다(오늘 두 번째로 걸린 함정이다).
   *   · 뒤의 것은 클래스 이름이 틀렸다 — `#my-board`가 실제로 다는 클래스는
   *     `board is-editing`이다(`console.js`). `.my-board`로 적힌 규칙은 아무 데도
   *     안 닿는다. */
  .world .board.is-editing .tile,
  .world .board.is-dragging .tile { transition: none; }
}

/* ======================================================================
 * 칸 밖으로 새는 글자 (2026-09-20)
 *
 * 운영자 제보는 "사이트 전체"였지만, 360·390px에서 열여섯 장을 재 보니
 * **자리 둘**이었다. 전역 안전판은 이미 있다 — `tokens.css`가 사이트 전체에
 * `word-break: keep-all; overflow-wrap: break-word`를 걸고, `world.css` V5-1·V5-3이
 * 표의 글 칸을 `white-space: normal; overflow-wrap: anywhere`로 푼다. 새는 것은
 * 안전판이 없어서가 아니라 **그 위에 얹힌 `nowrap`** 때문이었다:
 *
 *     .league-retired              문장에 nowrap       → 옆 칸 위로 +10~25px
 *     #krtg-table th[scope=row]    이름에 nowrap       → 옆 칸 위로 +9~40px
 *
 * 그래서 전역 `word-break: break-all` 처방을 쓰지 않는다 — 그건 한글을 음절에서
 * 끊어 새지 않던 문장 전부를 망가뜨린다(`keep-all`이 이 저장소의 판정이다).
 * ====================================================================== */

/* 공급자가 내린 자리의 사유는 **문장**이다. 이름 옆에 붙어 nowrap으로 서면
   표의 다음 열을 덮는다. 이름 아래 제 줄에 세운다 — 이름보다 작고 조용하다는
   원래 의도(league.css)는 그대로다. */
.world .league-retired { white-space: normal; overflow-wrap: anywhere; }
/* 표에서만 제 줄을 갖는다. 카드 머리(`.league-slip header h3`)는 넓어서 새지
   않으므로 그대로 이름 옆에 둔다 — 거기서 한 줄을 더 쓰면 같은 줄 옆 카드의
   메타 줄이 7.8px 어긋난다(자 실측 ALIGN, 2026-09-20). */
.world .league-page .league-table .league-retired { display: block; flex: 1 0 100%; margin-left: 0; }
/* 이름 링크는 `inline-flex`다(league.css의 44px 손가락 자리). 그 안에서 사유는
   플렉스 아이템이라 `display: block`만으로는 제 줄을 못 갖고, 줄어들다 못해
   이름 위로 겹쳐 앉는다(운영자 제보 2026-09-20, 데스크톱 밤 테마).
   **사유가 있는 줄에서만** 줄바꿈을 연다 — 전 줄에 열면 긴 이름의 로고가
   혼자 윗줄에 남는다(390px 실측: Nemotron 행). */
.world .league-page .league-table .league-name a:has(.league-retired) { flex-wrap: wrap; }

/* 붙이는 공통 클래스 둘. **기본값이 아니라 선언**이다 — 어디에 붙였는지가
   코드에 남아야 다음 사람이 왜 그 칸만 다른지 읽을 수 있다.
   · .u-wrap    : 좁은 칸 안에서 무슨 일이 있어도 접힌다(격자·플렉스 자식 포함).
   · .u-clamp-2 : 두 줄까지만 보이고 나머지는 말줄임. 값·출처·한계에는 쓰지 않는다
 *               (PRODUCT.md: 출처·한계를 접어서 시각적 정돈을 만들지 않는다). */
.world .u-wrap { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.world .u-clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
