/* 공개 상태 페이지 — 시각 언어
 *
 * 에테님이 준 디자인 파일(StatusPage.dc.html)의 값을 그대로 옮겼다.
 * 색·크기·라운드는 추정이 아니라 그 파일에서 읽은 값이다.
 *
 * 규칙 하나만 지키면 나머지는 따라온다: **이 페이지에서 튀는 색은 상태색뿐이다.**
 * 브랜드 강조색을 따로 쓰지 않는다(로고의 초록도 정상색과 같은 값이다).
 * 사용자가 이 페이지를 열었을 때 눈이 가야 할 곳은 로고나 버튼이 아니라
 * 색이 바뀐 점 하나다.
 *
 * 다크가 원본이고 라이트는 그 구조를 그대로 옮긴 짝이다. 상태색은 배경 명도가
 * 뒤집히면 같은 값으로 같은 강도가 나오지 않아서, 라이트에서는 한 단계 낮춘다.
 *
 * 폰트는 Geist / Geist Mono 를 쓰되 **렌더를 막지 않게** 불러온다(index.html).
 * 이 페이지는 다른 게 다 죽었을 때 열리는 페이지다. 폰트 CDN 이 안 떠도
 * 글자는 즉시 보여야 한다.
 */

:root {
  color-scheme: dark;

  /* 면 */
  --bg: #0a0a0b;
  --card: #121215;
  --card-2: #141417;
  --raised: #16161a;
  --border: #1d1d22;
  --border-2: #26262c;
  --border-3: #2a2a31;
  --hairline: #18181c;

  /* 글자 */
  --heading: #fafafa;
  --strong: #e6e6ea;
  --body: #aeaeb6;
  --text: #8a8a93;
  --text-dim: #6f6f78;
  --text-faint: #55555d;

  /* 상태 */
  --ok: #3ecf8e;
  --warn: #f5a623;
  --partial: #f97316;
  --down: #f0494a;
  --maint: #4f8cff;
  --none: #6f6f78;

  --ok-glow: rgba(62, 207, 142, .18);
  --warn-glow: rgba(245, 166, 35, .18);
  --down-glow: rgba(240, 73, 74, .18);
  --maint-glow: rgba(79, 140, 255, .18);
  --none-glow: rgba(154, 154, 162, .15);

  --ok-soft: rgba(62, 207, 142, .10);
  --ok-line: rgba(62, 207, 142, .25);
  --warn-soft: rgba(245, 166, 35, .12);
  --warn-line: rgba(245, 166, 35, .30);
  --down-soft: rgba(240, 73, 74, .12);
  --down-line: rgba(240, 73, 74, .30);
  --maint-soft: rgba(79, 140, 255, .12);
  --maint-line: rgba(79, 140, 255, .28);
  --none-soft: rgba(154, 154, 162, .10);
  --none-line: rgba(154, 154, 162, .22);

  /* 90일 블록에서 자료가 없는 칸. 상태색이 아니라 면 색 계열이다 —
     회색을 상태색으로 쓰면 "관측 없음" 이 하나의 상태처럼 보인다. */
  --bar-empty: #232329;

  --radius: 16px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --radius-xs: 8px;
  --wrap: 860px;

  --shadow-pop: 0 16px 48px rgba(0, 0, 0, .55);
}

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

  --bg: #fbfbfc;
  --card: #ffffff;
  --card-2: #ffffff;
  --raised: #f4f4f6;
  --border: #e6e6ea;
  --border-2: #dcdce2;
  --border-3: #d2d2da;
  --hairline: #eeeef1;

  --heading: #0e0e11;
  --strong: #1c1c21;
  --body: #4a4a53;
  --text: #6c6c76;
  --text-dim: #85858f;
  --text-faint: #9d9da6;

  --ok: #168a5f;
  --warn: #b8760a;
  --partial: #c2540d;
  --down: #c22f30;
  --maint: #2f62c9;
  --none: #8d8d97;

  --ok-glow: rgba(22, 138, 95, .14);
  --warn-glow: rgba(184, 118, 10, .14);
  --down-glow: rgba(194, 47, 48, .14);
  --maint-glow: rgba(47, 98, 201, .14);
  --none-glow: rgba(141, 141, 151, .14);

  --ok-soft: rgba(22, 138, 95, .08);
  --ok-line: rgba(22, 138, 95, .24);
  --warn-soft: rgba(184, 118, 10, .10);
  --warn-line: rgba(184, 118, 10, .28);
  --down-soft: rgba(194, 47, 48, .09);
  --down-line: rgba(194, 47, 48, .26);
  --maint-soft: rgba(47, 98, 201, .09);
  --maint-line: rgba(47, 98, 201, .24);
  --none-soft: rgba(141, 141, 151, .10);
  --none-line: rgba(141, 141, 151, .24);

  --bar-empty: #e8e8ec;
  --shadow-pop: 0 16px 48px rgba(14, 14, 17, .12);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    --bg: #fbfbfc;
    --card: #ffffff;
    --card-2: #ffffff;
    --raised: #f4f4f6;
    --border: #e6e6ea;
    --border-2: #dcdce2;
    --border-3: #d2d2da;
    --hairline: #eeeef1;

    --heading: #0e0e11;
    --strong: #1c1c21;
    --body: #4a4a53;
    --text: #6c6c76;
    --text-dim: #85858f;
    --text-faint: #9d9da6;

    --ok: #168a5f;
    --warn: #b8760a;
    --partial: #c2540d;
    --down: #c22f30;
    --maint: #2f62c9;
    --none: #8d8d97;

    --ok-glow: rgba(22, 138, 95, .14);
    --warn-glow: rgba(184, 118, 10, .14);
    --down-glow: rgba(194, 47, 48, .14);
    --maint-glow: rgba(47, 98, 201, .14);
    --none-glow: rgba(141, 141, 151, .14);

    --ok-soft: rgba(22, 138, 95, .08);
    --ok-line: rgba(22, 138, 95, .24);
    --warn-soft: rgba(184, 118, 10, .10);
    --warn-line: rgba(184, 118, 10, .28);
    --down-soft: rgba(194, 47, 48, .09);
    --down-line: rgba(194, 47, 48, .26);
    --maint-soft: rgba(47, 98, 201, .09);
    --maint-line: rgba(47, 98, 201, .24);
    --none-soft: rgba(141, 141, 151, .10);
    --none-line: rgba(141, 141, 151, .24);

    --bar-empty: #e8e8ec;
    --shadow-pop: 0 16px 48px rgba(14, 14, 17, .12);
  }
}

/* ── 기본 ───────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--body);
  font-family: "Geist", system-ui, -apple-system, "Segoe UI",
               "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
  font-size: 13.5px;
  line-height: 1.6;
  letter-spacing: -.01em;
  -webkit-font-smoothing: antialiased;
  word-break: keep-all;
}

::selection { background: var(--ok-glow); }

.mono, .mono * {
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
}

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--ok);
  outline-offset: 2px;
  border-radius: 4px;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.02em; color: var(--heading); }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }

/* 자료가 없을 때의 자리. 빈칸을 그냥 두지 않는다 —
   "없음" 과 "아직 안 그려짐" 이 화면에서 같아 보이면 안 된다. */
.empty {
  padding: 16px 18px;
  border: 1px dashed var(--border-2);
  border-radius: var(--radius-md);
  color: var(--text-dim);
  font-size: 13px;
}

/* ── 상단바 ─────────────────────────────────────────────── */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  height: 62px;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(14px);
}
@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: var(--bg); }
}

.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  height: 62px;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  font-size: 15px;
  font-weight: 600;
  color: var(--heading);
}
.brand:hover { text-decoration: none; }
.brand-sub { font-size: 13px; font-weight: 500; color: var(--text-dim); }

/* 로고. 안쪽 사각형이 배경색이라 가운데가 뚫린 것처럼 보인다. */
.brand-mark {
  align-self: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-xs);
  background: linear-gradient(150deg, var(--ok), #1f9c63);
  box-shadow: 0 0 0 1px var(--ok-line), 0 2px 8px var(--ok-glow);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-right: 4px;
}
.brand-mark::after {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 3px;
  background: #06140d;
}
:root[data-theme="light"] .brand-mark::after { background: #eafaf2; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .brand-mark::after { background: #eafaf2; }
}

.topbar-right { display: flex; align-items: center; gap: 14px; }

.topbar-stamp {
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 14px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border-3);
  background: var(--card-2);
  color: var(--strong);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.theme-toggle:hover { border-color: #3a3a43; background: #1b1b1f; }
:root[data-theme="light"] .theme-toggle:hover { border-color: var(--border-3); background: var(--raised); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle:hover { border-color: var(--border-3); background: var(--raised); }
}

main.wrap { padding-top: 40px; padding-bottom: 80px; }

/* ── 히어로 (전체 상태 배너) ────────────────────────────── */
/*
 * 점 하나가 맥동한다. 움직이는 것이 화면에 하나뿐이면 그게 곧 시선이고,
 * 그 하나는 "지금 상태" 여야 한다. prefers-reduced-motion 에서는 멈춘다.
 */

.hero {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 22px 24px;
  border-radius: var(--radius);
  border: 1px solid var(--border-3);
  background: var(--card);
  margin-bottom: 14px;
}

.hero-icon {
  position: relative;
  width: 14px;
  height: 14px;
  flex: none;
}
.hero-icon::before,
.hero-icon::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 99px;
  background: var(--none);
}
.hero-icon::before { animation: scRing 2s ease-out infinite; }
.hero-icon::after { animation: scPulse 2s ease-in-out infinite; }
.hero-glyph { display: none; }

.hero-body { flex: 1; }
.hero-headline { font-size: 19px; font-weight: 600; color: var(--heading); margin-bottom: 3px; }
.hero-meta { margin: 0; font-size: 13.5px; color: var(--text); line-height: 1.5; }

.hero[data-status="operational"] .hero-icon::before,
.hero[data-status="operational"] .hero-icon::after { background: var(--ok); }
.hero[data-status="degraded"] .hero-icon::before,
.hero[data-status="degraded"] .hero-icon::after { background: var(--warn); }
.hero[data-status="outage"] .hero-icon::before,
.hero[data-status="outage"] .hero-icon::after { background: var(--down); }
.hero[data-status="maintenance"] .hero-icon::before,
.hero[data-status="maintenance"] .hero-icon::after { background: var(--maint); }

.hero[data-status="operational"] { background: var(--ok-soft); border-color: var(--ok-line); }
.hero[data-status="degraded"] { background: var(--warn-soft); border-color: var(--warn-line); }
.hero[data-status="outage"] { background: var(--down-soft); border-color: var(--down-line); }
.hero[data-status="maintenance"] { background: var(--maint-soft); border-color: var(--maint-line); }
.hero[data-status="unknown"] { background: var(--none-soft); border-color: var(--none-line); }

@keyframes scPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@keyframes scRing { 0% { transform: scale(1); opacity: .5; } 100% { transform: scale(2.6); opacity: 0; } }

/* ── 배너 (신선도 경고) ─────────────────────────────────── */

.banner {
  margin: 0 0 14px;
  padding: 14px 18px;
  border-radius: var(--radius-md);
  border: 1px solid var(--warn-line);
  background: var(--warn-soft);
  color: var(--body);
  font-size: 13px;
  line-height: 1.6;
}
.banner-hard { border-color: var(--down-line); background: var(--down-soft); }

/* ── 탭 ─────────────────────────────────────────────────── */

.tabs {
  display: flex;
  gap: 4px;
  margin: 26px 0 20px;
  border-bottom: 1px solid var(--border);
}

.tab {
  appearance: none;
  border: 0;
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  padding: 10px 12px;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.tab:hover { color: var(--strong); }
.tab[aria-selected="true"] { color: var(--heading); font-weight: 600; border-bottom-color: var(--ok); }

/* ★ .panel 에 display 를 주면 안 된다. 탭 전환은 hidden 속성으로 하는데,
 *   클래스 선택자가 UA 의 [hidden] 규칙을 이겨서 모든 패널이 함께 보인다.
 *   실제로 그렇게 깨졌다. hidden 을 명시적으로 이겨 두는 쪽이 안전하다. */
[hidden] { display: none !important; }

/* ── 섹션 ───────────────────────────────────────────────── */

.section-title {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 34px 0 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: .07em;
}
.section-title:first-child { margin-top: 0; }
.section-title .count {
  font-size: 11px;
  color: var(--text-faint);
  text-transform: none;
  letter-spacing: 0;
}

.stack { display: flex; flex-direction: column; gap: 10px; }

/* ── 카드 ───────────────────────────────────────────────── */

.card {
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--card);
  overflow: hidden;
}

/* 머리줄은 세로 두 층. 윗줄에 제목·시각·자세히, 아랫줄에 영향 대상.
 * 예전에는 셋을 한 줄에 두고 각각 margin-left:auto 를 줬는데, 줄바꿈이
 * 일어나면 조각마다 따로 오른쪽 끝에 붙어 계단처럼 어긋났다. */
.card-head {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 17px 20px;
}
.card-topline { display: flex; align-items: center; gap: 12px; min-width: 0; }
.card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--heading);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.card-sub {
  margin-left: auto;
  font-size: 12px;
  /* 흐리게 두면 발생 시각·경과 시간이 안 읽힌다. 이 줄이 사건 카드에서
   * 제일 자주 찾는 값이다. */
  color: var(--strong);
  white-space: nowrap;
}
.card-body { padding: 8px 22px 18px; }

/* ── 배지 · 칩 ──────────────────────────────────────────── */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 24px;
  padding: 0 10px;
  border-radius: 99px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--none-line);
  background: var(--none-soft);
  color: var(--none);
}
.badge[data-status="operational"] { border-color: var(--ok-line); background: var(--ok-soft); color: var(--ok); }
.badge[data-status="degraded"] { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn); }
.badge[data-status="outage"] { border-color: var(--down-line); background: var(--down-soft); color: var(--down); }
.badge[data-status="maintenance"] { border-color: var(--maint-line); background: var(--maint-soft); color: var(--maint); }

.badge-plain {
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 9px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 500;
  color: var(--strong);
  background: var(--raised);
  border: 1px solid var(--border);
}

.chip-dot {
  width: 9px;
  height: 9px;
  border-radius: 99px;
  flex: none;
  background: var(--none);
  box-shadow: 0 0 0 3px var(--none-glow);
}
.chip-dot[data-status="operational"] { background: var(--ok); box-shadow: 0 0 0 3px var(--ok-glow); }
.chip-dot[data-status="degraded"] { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-glow); }
.chip-dot[data-status="outage"] { background: var(--down); box-shadow: 0 0 0 3px var(--down-glow); }
.chip-dot[data-status="maintenance"] { background: var(--maint); box-shadow: 0 0 0 3px var(--maint-glow); }
.chip-dot[data-status="unknown"] { background: var(--none); box-shadow: 0 0 0 3px var(--none-glow); }

/* ── 필터 칩 ────────────────────────────────────────────── */

.filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }

.chip {
  appearance: none;
  height: 30px;
  padding: 0 13px;
  border-radius: 99px;
  border: 1px solid var(--border-3);
  background: var(--card-2);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
}
.chip:hover { color: var(--strong); border-color: #3a3a43; }
.chip[aria-pressed="true"] { color: var(--heading); border-color: var(--ok-line); background: var(--ok-soft); }

/* ── 사건 · 업데이트 타임라인 ───────────────────────────── */

/* 제목 아래 줄, **왼쪽** 정렬. 개수가 늘면 아래로 흐른다. */
.affected {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 12px;
  color: var(--strong);
}

/* 본문(.card-body)과 **같은 좌우 여백**을 준다. 이 블록은 카드에 직접
 * 붙어서 여백을 물려받지 못하는데, 그러면 레일의 점만 카드 왼쪽 끝으로
 * 튀어나가 본문과 어긋난다. */
.updates { display: flex; flex-direction: column; padding: 0 22px 8px; }

.update { display: flex; gap: 14px; padding: 12px 0; }
.update-rail { display: flex; flex-direction: column; align-items: center; padding-top: 4px; }
.update-line { width: 1px; flex: 1; background: var(--border-2); margin-top: 4px; }
.update-body { flex: 1; padding-bottom: 2px; }
.update-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.update-phase { font-size: 13px; font-weight: 600; color: var(--strong); }
.update-phase[data-status="operational"] { color: var(--ok); }
.update-phase[data-status="degraded"] { color: var(--warn); }
.update-phase[data-status="outage"] { color: var(--down); }
.update-phase[data-status="maintenance"] { color: var(--maint); }
.update-at { font-size: 12px; color: var(--strong); }
.update p { margin: 0; font-size: 13.5px; color: var(--strong); line-height: 1.6; }
/* 여러 문단이 붙어 나오면 서로 달라붙는다. 첫 문단만 여백이 없다. */
.update-body p + p, .update-body ul { margin-top: 7px; }
.update-body ul { margin-bottom: 0; padding-left: 18px; font-size: 13.5px; color: var(--body); }
.update-rail .chip-dot { flex: none; }

/* ── 본문 블록 ──────────────────────────────────────────── */

.card-body p { margin: 0 0 10px; font-size: 13.5px; color: var(--strong); line-height: 1.6; }
.card-body ul { margin: 0 0 10px; padding-left: 18px; color: var(--strong); font-size: 13.5px; }
.card-body li { margin-bottom: 4px; }
.card-body li::marker { color: var(--body); }

/*
 * 렌더러의 kv 노드(["라벨","값"] 쌍). dl 기본 스타일은 dd 를 들여써서
 * 라벨과 값이 계단처럼 어긋난다. "처음 확인 / 영향" 같은 쌍은 눈으로
 * 훑는 값이라 두 열로 나란히 둔다.
 */
.kv {
  display: grid;
  grid-template-columns: minmax(84px, max-content) 1fr;
  gap: 5px 18px;
  margin: 12px 0;
  padding: 13px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--raised);
  font-size: 13px;
}
.kv dt { color: var(--strong); }
.kv dd { margin: 0; color: var(--strong); }

/* ── 서비스 그룹 · 90일 블록 ────────────────────────────── */

.group-head {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 17px 20px;
  cursor: pointer;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  user-select: none;
}
.group-head:hover { background: var(--card-2); }
.group-head .row-name { font-size: 14.5px; font-weight: 600; color: var(--strong); }

/* 캐럿과 글자를 세로 가운데로 맞춘다.
 * inline 배치에서는 삼각형(높이 8px)이 글자 **밑선**에 앉아서 한 칸 내려가
 * 보인다. flex + align-items:center 로 광학 중심을 맞춘다. */
.expander {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  line-height: 1;
}
.expander-label { line-height: 1; }
.expander-caret {
  display: block;
  flex: none;
  width: 0;
  height: 0;
  border-left: 5px solid var(--text-dim);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform .15s ease;
}
[aria-expanded="true"] .expander-caret { transform: rotate(90deg); }

.uptime {
  margin-left: auto;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--body);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* 값이 없는 자리(집계 전 · 비공개). 숫자와 같은 무게로 두면 없는 실적이
 * 있는 실적처럼 읽힌다. */
.uptime.muted { color: var(--text-dim); font-weight: 400; }
/* 성능 저하 비율 — 가동률 옆에 낮은 대비로 붙는다. 주인공은 가동률이다. */
.uptime-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--warn);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  opacity: .85;
}
/* .uptime 이 margin-left:auto 로 오른쪽 끝에 붙으므로, 뒤따르는 저하 표시는
 * 자동 여백을 다시 만들면 안 된다(그러면 둘이 양 끝으로 벌어진다). */
.uptime + .uptime-sub { margin-left: 0; }

.row { padding: 0 20px 16px; }

/* ★ .row-main 은 세로 컨테이너다 — 이름줄과 90일 바를 함께 담는다.
 *   가로 flex 로 만들었더니 이름이 "TTS / 음성 / 합성" 으로 줄바꿈되고
 *   바가 옆에 붙었다. 이름줄만 가로로 두고 바는 줄을 바꿔 내린다. */
.row-main { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.row-main > .bar90 { flex: 1 0 100%; margin-top: 9px; }
.row-main > .bar90-scale { flex: 1 0 100%; }

.row-label { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.row-name { font-size: 13px; color: var(--strong); }
.row-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* 90일 블록.
 * ★ 자료가 없는 칸은 --bar-empty(면 색)다. 상태색 회색을 쓰면 "관측 없음" 이
 *   하나의 상태처럼 읽힌다. 없는 것은 없는 것으로 보여야 한다. */
.bar90 { display: flex; gap: 2px; height: 30px; align-items: stretch; }
.bar90-cell {
  flex: 1 1 0;
  min-width: 4px;
  border-radius: 2.5px;
  background: var(--bar-empty);
  transition: opacity .12s;
}
.bar90-cell:hover { opacity: .6; }
.bar90-cell[data-status="operational"] { background: var(--ok); }
.bar90-cell[data-status="degraded"] { background: var(--warn); }
.bar90-cell[data-status="outage"] { background: var(--down); }
.bar90-cell[data-status="maintenance"] { background: var(--maint); }

.bar90-scale {
  display: flex;
  justify-content: space-between;
  margin-top: 7px;
  font-size: 11px;
  color: var(--text-faint);
}

/* 펼친 컴포넌트 */
/* ── '자세히' 펼침 애니메이션 ─────────────────────────────
 *
 * 투명도만 바꾸면 아래 내용이 툭 튀어 오른다. 높이도 같이 움직여야 하는데
 * 내용 높이를 미리 모르므로 grid-template-rows 0fr→1fr 을 쓴다 —
 * max-height 를 어림잡는 옛 수법과 달리 내용이 길어도 안 잘린다.
 *
 * 접을 때는 app.js 가 features-leaving 을 붙이고 애니메이션이 끝난 뒤에
 * 다시 그린다(다시 그리면 요소가 사라져 transition 이 걸릴 데가 없다).
 */
@keyframes features-in {
  from { opacity: 0; grid-template-rows: 0fr; }
  to   { opacity: 1; grid-template-rows: 1fr; }
}
@keyframes features-out {
  from { opacity: 1; grid-template-rows: 1fr; }
  to   { opacity: 0; grid-template-rows: 0fr; }
}

.features {
  border-top: 1px solid var(--border);
  display: grid;
  grid-template-rows: 1fr;
  overflow: hidden;              /* 접히는 동안 내용이 삐져나오지 않게 */
}
/* ★ 애니메이션은 **방금 연 줄** 에만. .features 에 걸어 두면 화면을 다시
 *   그릴 때마다 이미 열려 있던 줄도 같이 재생돼, 두 개를 열어 두고 하나를
 *   닫을 때 나머지가 같이 튄다. */
.features-entering {
  animation: features-in .22s cubic-bezier(.22, .61, .36, 1) both;
}
/* 여백은 **안쪽**이 갖는다. 바깥에 두면 높이가 0 이 되어도 여백이 남는다. */
.features-inner { padding: 6px 20px 14px; min-height: 0; }
.features-leaving { animation: features-out .2s cubic-bezier(.55, .06, .68, .19) both; }

/* 캐럿도 같이 돈다 */
.expander-caret { transition: transform .2s cubic-bezier(.22, .61, .36, 1); }

@media (prefers-reduced-motion: reduce) {
  .features-entering, .features-leaving { animation: none; }
  .expander-caret { transition: none; }
}
/* 이름 · 막대 · 수치를 한 줄에 나란히 둔다.
 * 이름 칸은 고정폭 하한을 줘서, 항목 이름이 길어도 막대가 밀리지 않게 한다 —
 * 항목마다 막대 폭이 달라지면 같은 날짜가 세로로 안 맞아서 비교가 안 된다. */
.feature-row {
  display: grid;
  /* 이름 칸은 **고정폭**. 이름 길이에 따라 칸이 늘면 항목마다 막대 폭이
   * 달라져서 같은 날짜가 세로로 안 맞는다. 남는 폭은 전부 막대가 먹는다 —
   * 이름 오른쪽부터 퍼센트 왼쪽까지 꽉 찬다. */
  /* 오른쪽 칸도 **고정폭**. auto 로 두면 "100%" 와 "99.29%" 의 글자 폭 차이가
   * 그대로 칸 폭 차이가 되어, 막대 오른쪽 끝이 줄마다 어긋난다. */
  grid-template-columns: 160px minmax(0, 1fr) 124px;
  align-items: center;
  gap: 10px 14px;
  padding: 10px 0;
}
/* ★ 자리를 **명시**한다. 응답 속도가 둘째 줄을 통째로 쓰기 때문에, 자동 배치에
 *   맡기면 상태 배지가 그 줄로 밀려 내려간다. */
.feature-head { grid-column: 1; grid-row: 1; }
.feature-row > .bar90, .feature-bar-empty { grid-column: 2; grid-row: 1; }
.feature-tail { grid-column: 3; grid-row: 1; }

/* ── 응답 속도 (에테님 요청, 2026-09-17) ─────────────────── */
/* 응답 속도는 **그 항목 줄 아래를 통째로** 쓴다(에테님, 2026-09-17:
 * "옆이 아니라, 아래에 조금 크게"). 옆 칸에 두면 60~120 표본이 몇십 px 에
 * 뭉개져서 장식이 되고, 정작 느려지는 순간이 안 보인다.
 *
 * ★ 안쪽을 **바깥과 같은 3열**로 짠다. 그래야 그래프의 좌우 끝이 위 90일 막대와
 *   정확히 맞는다 — 어긋나면 "그날 왜 느렸나" 를 세로로 훑을 수 없다. */
.feature-latency {
  grid-column: 1 / -1;
  grid-row: 2;
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr) 124px;
  gap: 14px;
  align-items: center;
  margin-top: 4px;
  min-width: 0;
}
.latency-label {
  font-size: 11.5px;
  color: var(--text-faint);
  white-space: nowrap;
}
/* 숫자 아래에 '언제 값인지' 가 붙을 수 있어 세로로 쌓는다. 가로로 두면
 * "1,750 ms 6시간 전" 이 칸을 넘쳐 그래프 위로 올라탄다. */
.latency-now {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  text-align: right;
  white-space: nowrap;
}
.latency-val {
  font-size: 13px;
  font-weight: 500;
  color: var(--strong);
  font-variant-numeric: tabular-nums;
}
/* 계속 쓰이지 않는 제공사의 마지막 측정값. 숫자와 같은 무게로 두면 지금 값처럼 읽힌다. */
.latency-stale {
  font-style: normal;
  font-size: 10.5px;
  color: var(--text-faint);
}

/* 그래프 상자. 눈금 글자는 전부 HTML 이고 **`.chart-plot` 기준으로 절대배치**된다.
 * 그림 영역과 글자의 기준이 같아야 호버 계산(마우스 x → 몇 번째 점)이 맞는다. */
.latency-chart { position: relative; height: 104px; min-width: 0; }
.chart-plot {
  position: absolute;
  /* ★ left 는 **0** 이어야 한다. 이 칸은 위 90일 막대와 같은 그리드 열이라,
   *   여기에 눈금 자리를 만들면 그래프만 안쪽으로 밀려 막대와 좌우가 어긋난다
   *   (실측: bar90 442..860 인데 plot 488..860 이 됐다). y축 글자는 왼쪽
   *   **바깥**(-46px)으로 내보낸다 — 이름 칸에 그만한 빈자리가 있다. */
  left: 0; right: 0; top: 6px; bottom: 18px;
  border-bottom: 1px solid var(--border);
}
/* ★ overflow 를 숨기면 안 된다 — y축 글자(왼쪽 바깥)와 말풍선이 상자 밖에 있다. */
.chart-plot svg { display: block; width: 100%; height: 100%; overflow: visible; }
.chart-line {
  fill: none;
  stroke: var(--maint);
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.chart-grid { stroke: var(--hairline); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-y {
  position: absolute;
  left: -46px; width: 40px;
  text-align: right;
  font-size: 10px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transform: translateY(-50%);
}
.chart-x {
  position: absolute;
  bottom: -16px;
  font-size: 10px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transform: translateX(-50%);
}
.chart-cursor {
  position: absolute; top: 0; bottom: 0; width: 0;
  border-left: 1px dashed var(--text-dim);
  pointer-events: none;
}
.chart-dot {
  position: absolute; width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--maint);
  box-shadow: 0 0 0 2px var(--card);
  pointer-events: none;
}
.chart-tip {
  position: absolute; z-index: 2;
  transform: translate(-50%, -100%);
  background: var(--raised);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 5px 9px;
  font-size: 11px;
  color: var(--strong);
  white-space: nowrap;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
  pointer-events: none;
}
.chart-tip .tip-time { color: var(--text-dim); margin-right: 8px; }
.chart-tip .tip-val { font-variant-numeric: tabular-nums; font-weight: 500; }
.feature-row + .feature-row { border-top: 1px solid var(--hairline); }
.feature-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.feature-name { font-size: 13px; color: var(--body); }
.feature-tail {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-end;
  /* 폭을 고정한다. 그냥 두면 "100%" 와 "99.29%" 의 글자 폭 차이가 그대로
   * 칸 폭 차이가 되어, 왼쪽 막대의 오른쪽 끝이 줄마다 어긋난다. */
  width: 124px;
  flex: 0 0 124px;
}
.feature-uptime {
  font-size: 12px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  /* 칸 폭은 위 격자가 고정했다. 여기서는 숫자만 오른쪽으로 붙인다. */
  text-align: right;
  white-space: nowrap;
}
/* 컴포넌트 막대(30px)보다 확실히 낮게. 세부 항목이 본 줄보다 튀면 안 된다. */
.feature-row .bar90 { height: 10px; gap: 1px; }
.feature-row .bar90-cell { border-radius: 1.5px; min-width: 2px; }
.feature-bar-empty { height: 10px; }

/* 좁은 화면에서만 막대를 아래로 내린다. 3열을 우겨넣으면 이름이 잘린다. */
@media (max-width: 640px) {
  .feature-row { grid-template-columns: 1fr auto; }
  .feature-tail { grid-column: 2; }
  .feature-row .bar90, .feature-bar-empty { grid-column: 1 / -1; grid-row: 2; }
  /* 좁은 화면에서는 막대 아래 한 줄을 통째로 쓴다. 3~4열을 우겨넣으면
   * 스파크라인이 몇 픽셀로 찌그러져서 읽을 수 없는 장식이 된다. */
  .feature-latency {
    grid-row: 3;
    grid-template-columns: auto 1fr;
  }
  .latency-label { grid-column: 1; grid-row: 1; }
  .latency-now { grid-column: 2; grid-row: 1; }
  /* 좁은 화면에서는 그래프가 줄을 하나 더 쓴다. 라벨·숫자와 폭을 다투게 두면
   * 셋 다 읽을 수 없게 찌그러진다. */
  .latency-chart { grid-column: 1 / -1; grid-row: 2; height: 92px; }
  /* 좁은 화면에서는 그래프가 카드 왼쪽 끝까지 온다. 그대로 두면 y축 글자가
   * 카드 밖으로 나가 잘린다 — 여기서만 눈금 자리를 안쪽에 둔다. */
  .chart-plot { left: 46px; }
}

/* ── 요약 목록 (개요 탭) ────────────────────────────────── */

.summary-group { border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--card); overflow: hidden; }
.summary-group > .section-title { margin: 0; padding: 14px 20px; background: var(--raised); border-bottom: 1px solid var(--border); }
.summary-row { display: flex; align-items: center; gap: 11px; padding: 13px 20px; }
.summary-row + .summary-row { border-top: 1px solid var(--hairline); }
.summary-name { font-size: 13.5px; color: var(--strong); }

/* ── 역사 ───────────────────────────────────────────────── */

.month { margin-bottom: 22px; }
.month > h3 {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-dim);
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--hairline);
}
.month-empty { font-size: 12.5px; color: var(--text-faint); padding: 4px 0 12px; }
.day { margin-bottom: 14px; }
.day-date { font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }
.day-items { display: flex; flex-direction: column; gap: 12px; }

/* ── 범례 ───────────────────────────────────────────────── */

.legend { display: flex; flex-wrap: wrap; gap: 9px 18px; margin-top: 16px; padding: 0 2px; }
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-dim); }
/* 점은 마크업의 .chip-dot 이 그린다. ::before 를 두면 점이 두 개가 된다. */
.legend-item .chip-dot { width: 8px; height: 8px; box-shadow: none; }
.legend-item .chip-dot[data-status="unknown"] { background: var(--bar-empty); box-shadow: 0 0 0 1px var(--border-2); }

/* ── 푸터 ───────────────────────────────────────────────── */

.footer {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--hairline);
  color: var(--text-faint);
  font-size: 12px;
}
.footer-note { margin: 10px 0 0; color: var(--text-dim); line-height: 1.6; }

/* ── 반응형 · 접근성 ────────────────────────────────────── */

@media (max-width: 620px) {
  .wrap { padding: 0 16px; }
  main.wrap { padding-top: 28px; }
  .hero { padding: 18px 18px; gap: 14px; }
  .hero-headline { font-size: 17px; }
  .card-head, .group-head, .row { padding-left: 16px; padding-right: 16px; }
  .card-body { padding-left: 16px; padding-right: 16px; }
  .features-inner, .summary-row { padding-left: 16px; padding-right: 16px; }
  .bar90 { height: 26px; }
  .bar90-cell { min-width: 2px; }
  .topbar-stamp { display: none; }   /* 좁은 화면에서는 히어로의 시각만 남긴다 */
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* 머리줄 아무 데나 눌러도 펼쳐진다. 그 사실을 커서로 알린다 —
 * 눌러도 되는지 모르면 아무도 안 누른다. */
.expandable { cursor: pointer; }
.expandable :is(a, button, input, select, textarea) { cursor: auto; }
.expandable .expander { cursor: pointer; }
