.admin-layout { display: grid; grid-template-columns: minmax(160px, 220px) minmax(0, 1fr); gap: 2rem; width: 100%; max-width: 1180px; margin: 0 auto; }
/* The grid owns width, including empty, loading and error states. */
.admin-layout > .admin-shell { width: 100%; max-width: none; min-width: 0; margin: 0; }
.admin-sidebar { padding: 2rem 0 0 1rem; }
.admin-sidebar nav { display: grid; gap: .65rem; position: sticky; top: 1rem; }
.admin-sidebar a { color: inherit; text-decoration: none; padding: .55rem .7rem; border-left: 2px solid transparent; }
.admin-sidebar a:hover, .admin-sidebar a:focus, .admin-sidebar a.is-active { border-color: currentColor; background: color-mix(in srgb, currentColor 8%, transparent); }
.admin-panel { margin-top: 1.5rem; }

/* ---------------------------------------------------------------------------
   Overview loading skeleton.

   The dashboard needs a session round-trip and then a dashboard round-trip
   before it can render a single card, and #admin-summary used to be an empty
   div for that whole window: measured live at 1440 KO, the summary stayed 0px
   for ~2.0s and then became 1277px in one frame, moving the document from
   957px to 1687px.

   The first skeleton only reserved a heading, a parallel card grid and four
   12px bars, which left the two analytics sections unreserved. Measured live
   on production at 1440 EN with an authenticated admin session, #admin-summary
   is 1361px made of H2 26 + .dashboard-grid 480 + .admin-analytics 401 +
   .admin-analytics.admin-d1-usage 397, so 798px of the collapse survived.

   The reservation is therefore structural: the skeleton reuses the real
   .dashboard-grid / .dashboard-card / .admin-analytics selectors and fills
   them with line-box placeholders sized in `lh`, so each placeholder occupies
   exactly the line the real text will occupy. Nothing here is an arbitrary
   min-height, and the reservation tracks font size, column count and viewport
   automatically because it is the production layout with the text removed.
   The markup carries no operational value, so it is safe in the
   pre-authorization document.
   --------------------------------------------------------------------------- */
.admin-skeleton { display: block; }
.admin-skeleton { width: 100%; max-width: 100%; }
.admin-skeleton-table { display: grid; gap: .55rem; max-width: 46rem; }
.admin-skeleton-block, .admin-skeleton-card { display: block; overflow: hidden; border-radius: var(--panel-radius); background: var(--row-hover); }
.admin-skeleton-block { height: 12px; border-radius: 4px; }
.admin-skeleton-heading { width: min(18rem, 42%); height: 18px; }
/* Line placeholders: the px value is the fallback for engines without `lh`. */
.admin-skeleton-line, .admin-skeleton-lines-2, .admin-skeleton-lines-3 {
  display: block;
  overflow: hidden;
  border-radius: 4px;
  background: var(--row-hover);
}
.admin-skeleton-line { height: 16px; height: 1lh; }
.admin-skeleton-lines-2 { height: 32px; height: 2lh; }
.admin-skeleton-lines-3 { height: 48px; height: 3lh; }
/* Placeholder text nodes keep their own line box but must not paint glyphs. */
.admin-skeleton :is(h2, h3, p, small, li, th, td) > .admin-skeleton-line,
.admin-skeleton :is(h2, h3, p, small, li, th, td) > .admin-skeleton-lines-2,
.admin-skeleton :is(h2, h3, p, small, li, th, td) > .admin-skeleton-lines-3 { width: 100%; }
.admin-skeleton .admin-skeleton-line-short { width: min(18rem, 42%); }
.admin-skeleton ul { margin: 0; padding-left: 1.2rem; }
.admin-skeleton-card { min-height: 8.5rem; border: var(--panel-border); }
/* Reserved cards keep the REAL label and detail glyphs so the browser counts
   their line boxes exactly as it will in the settled state (that is what makes
   the reservation locale-correct without a per-language number). The glyphs
   must not be visible before authorization, so they are painted transparent —
   this is a colour, not a clip: nothing is truncated or hidden. */
.admin-skeleton-text { color: transparent; background: var(--row-hover); border-radius: 4px; }
/* The value is live data, so its placeholder is a bar whose height comes from
   the same custom property that sizes the real value slot. */
.admin-skeleton-value { display: block; overflow: hidden; border-radius: 4px; background: var(--row-hover); height: calc(var(--dashboard-card-value-lines) * 1lh); }
.admin-skeleton-block::after, .admin-skeleton-line::after, .admin-skeleton-lines-2::after, .admin-skeleton-lines-3::after, .admin-skeleton-value::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--skeleton-highlight), transparent);
  animation: admin-skeleton-shimmer 1.4s ease-in-out infinite;
}
/* The card is now a real container of placeholders, so shimmering the card
   itself would paint over its children. Only the leaf placeholders shimmer. */
.admin-skeleton-card::after { content: none; }
/* The analytics prose sits in a 276px column on mobile instead of 644px, so
   the same sentences wrap to one more line than on desktop. Measured live at
   360 in BOTH languages, so this is column width, not translation length:
   .admin-analytics-meta 17px reserved vs 34px real, and the D1 lead paragraph
   38px vs 57px. Reserving one extra line each is the structural answer; it is
   not a per-language nudge. */
@media (max-width: 700px) {
  .admin-skeleton .admin-analytics-meta > .admin-skeleton-line { height: 32px; height: 2lh; }
  .admin-skeleton .admin-d1-usage p > .admin-skeleton-lines-2 { height: 48px; height: 3lh; }
  .admin-skeleton .admin-d1-usage p > .admin-skeleton-lines-3 { height: 95px; height: 5lh; }
}
@keyframes admin-skeleton-shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  .admin-skeleton-block::after,
  .admin-skeleton-card::after,
  .admin-skeleton-line::after,
  .admin-skeleton-lines-2::after,
  .admin-skeleton-lines-3::after,
  .admin-skeleton-value::after { animation: none; transform: none; background: none; }
}
#admin-retry { margin-top: .75rem; }
.admin-operational-shortcuts { margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.admin-operational-shortcuts h2 { margin: 0 0 .75rem; }
.admin-operational-shortcuts ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: .75rem; margin: 0; padding: 0; list-style: none; }
.admin-operational-shortcuts li { border: var(--panel-border); border-radius: var(--panel-radius); padding: var(--panel-padding-compact); }
.admin-operational-shortcuts a { color: inherit; font-weight: 700; }
.admin-operational-shortcuts small { display: block; margin-top: .35rem; color: var(--muted); line-height: 1.4; }

.dashboard-card { border: var(--panel-border); border-radius: var(--panel-radius); padding: var(--panel-padding); min-height: 8.5rem; }
/* ---------------------------------------------------------------------------
   Card narrative line-count contract — declared once, consumed twice.

   The previous reservation was an anonymous placeholder bar per card slot, so
   the loading state and the settled state laid out different text. Measured
   live on 43541f0 with the real dashboard GET held at Fetch
   requestStage=Request, the card grid's bottom moved loading -> settled by
   +7.2px at en-1440 but -56.7px at ko-1440: opposite directions, so no single
   reserved pixel height fixes both.

   The cause is that the two states laid out *different text*. The skeleton had
   anonymous placeholder bars while renderOverview supplied the real label and
   detail strings, so the browser counted a different number of line boxes in
   each state. The fix removes the divergence rather than tuning the numbers:
   admin.html declares each card's static label/detail copy and the reserved
   line count of its live-data value slot, and admin.js renders from that same
   declaration. Label and detail therefore wrap identically in both states
   because they are the same glyphs at the same width, and the value
   reservation resolves from this one custom property in both states.

   Defaults are 1 line. In practice every card declares 2, because a value is a
   count, an ISO timestamp, or an operational-reason sentence depending on the
   response, and .dashboard-grid rows equalize to their tallest card — so one
   wrapped value moved the whole row. Detail sentences are static copy and need
   no declaration — the same glyphs wrap the same in both states — except
   `lastCollection`, whose detail interpolates a live collector id: measured at
   en-1440 that pushes its real detail to 34.3px against 14px reserved, so it
   declares 2 lines.
   --------------------------------------------------------------------------- */
.dashboard-card { --dashboard-card-value-lines: 1; --dashboard-card-detail-lines: 1; }
.dashboard-card[data-card-value-lines="2"] { --dashboard-card-value-lines: 2; }
.dashboard-card[data-card-detail-lines="2"] { --dashboard-card-detail-lines: 2; }
.dashboard-card > p { min-height: calc(var(--dashboard-card-value-lines) * 1lh); }
.dashboard-card > small { display: block; min-height: calc(var(--dashboard-card-detail-lines) * 1lh); }
.dashboard-card h3 { margin: 0; font-size: .78rem; letter-spacing: .04em; text-transform: uppercase; }
.dashboard-card p { margin: .65rem 0 .35rem; font-size: 1.35rem; font-weight: 700; overflow-wrap: anywhere; }
.dashboard-card small, .admin-analytics-note { color: var(--muted); line-height: 1.45; }
#admin-overview .dashboard-card { min-height: 7.25rem; padding: var(--panel-padding-compact); }
#admin-overview .dashboard-card p { margin: var(--space-sm) 0 var(--space-xs); }
.admin-analytics-note { margin: 1rem 0 0; max-width: 70ch; }
.admin-analytics { margin-top: 1.5rem; max-width: 46rem; overflow-x: auto; }
.admin-analytics h2 { margin-bottom: .4rem; }
.admin-analytics table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.admin-analytics th, .admin-analytics td { border-bottom: 1px solid color-mix(in srgb, currentColor 22%, transparent); padding: .45rem .25rem; text-align: left; white-space: nowrap; }
.admin-analytics-meta { color: var(--muted); font-size: .9rem; line-height: 1.45; min-height: 17px; min-height: 1lh; }
/* ---------------------------------------------------------------------------
   Live-status line-count contract — the same "declare once, consume twice"
   shape as the card contract above, applied to the two blocks that were still
   moving at 360 after b73c3f3.

   #admin-status is a single element whose *text* changes: while loading it
   says `운영 데이터를 불러오는 중…` / `Loading operational data…`, and on settle
   the renderer replaces it with `업데이트됨: <ISO timestamp>` / `Updated <ISO
   timestamp>`. Measured live on 761e9fa with the real dashboard GET held at
   Fetch requestStage=Request: at 1440 both sentences are one 20.3px line box,
   but in the 360 column (1lh = 18.84px) the loading sentence is one line while
   the settled one is two — 18.84 -> 37.69, and every anchor below it (grid
   bottom, analytics h2, table top) moved by that same +18.8px.

   There is no room to shorten the sentence and no reason to delay the update,
   so the element declares the line count of its own worst case and both states
   are laid out against that floor. This is a measured maximum, not padding:
   two lines is exactly what the settled sentence occupies at 360.
   --------------------------------------------------------------------------- */
#admin-status { --admin-status-lines: 1; min-height: calc(var(--admin-status-lines) * 1lh); }
.admin-d1-lede { min-height: 38px; min-height: 2lh; }
.admin-d1-note { min-height: 57px; min-height: 3lh; }
/* The D1 account list is the `ul` the previous epic left unreserved because
   its overrun looked language-dependent. It is not the list that wraps — it is
   each item, whose text is `<label>: <value> / <limit> (<percent>%)`. Measured
   at 360: reserved 75.38 (4 lines on the ul) against a settled 94.22 in ko and
   113.06 in en, i.e. 5 and 6 line boxes across three items. Declaring the
   count on the *item* removes the language dependence, because both states
   then give every item the same floor and the renderer emits exactly the three
   items the reservation ships. */
.admin-d1-account { --admin-d1-account-lines: 1; min-height: 57px; min-height: 3lh; }
.admin-d1-account > li { min-height: calc(var(--admin-d1-account-lines) * 1lh); }
/* ---------------------------------------------------------------------------
   Overview charts (native SVG, no dependency).
   차트가 색을 직접 갖지 않고 테마 토큰(--chart-series-*, --line, --muted,
   --row-hover)만 참조하는 이유는 6개 테마·라이트/다크 조합 전부에서 선과
   배경 대비가 이미 측정된 토큰 값을 상속하게 하기 위해서다. 범례·빈 상태·
   접힌 데이터 표는 같은 패널의 DOM 텍스트라 기존 대비 규칙을 그대로 따른다.
   --------------------------------------------------------------------------- */
.admin-chart-panel { border: var(--panel-border); border-radius: var(--panel-radius); background: var(--surface); padding: var(--panel-padding); }
.admin-chart-figure { display: grid; gap: var(--space-sm); margin: var(--space-md) 0 0; min-width: 0; }
.admin-chart-figure svg { width: 100%; height: auto; display: block; }
.admin-chart-gridline { stroke: var(--line); stroke-width: 1; }
.admin-chart-axis-label, .admin-chart-bar-value { fill: var(--muted); font-size: 12px; font-family: inherit; }
.admin-chart-bar-label { fill: var(--ink); font-size: 12px; font-family: inherit; }
.admin-chart-line { fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.admin-chart-point { stroke: var(--surface); stroke-width: 1.5; }
.admin-chart-series-visits { stroke: var(--chart-series-0); fill: var(--chart-series-0); }
.admin-chart-series-pageviews { stroke: var(--chart-series-1); fill: var(--chart-series-1); }
.admin-chart-line { fill: none; }
.admin-chart-legend { display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg); margin: 0; padding: 0; list-style: none; font-size: var(--text-label-size); color: var(--ink); }
.admin-chart-legend li { display: inline-flex; align-items: center; gap: 6px; }
.admin-chart-legend li::before { content: ""; width: 12px; height: 12px; border-radius: 3px; background: var(--legend-color); }
.admin-chart-legend .admin-chart-series-visits { color: var(--chart-series-0); --legend-color: var(--chart-series-0); }
.admin-chart-legend .admin-chart-series-pageviews { color: var(--chart-series-1); --legend-color: var(--chart-series-1); }
.admin-chart-bar-track { fill: var(--row-hover); stroke: var(--line); stroke-width: 1; }
.admin-chart-bar { fill: var(--chart-series-2); }
.admin-chart-empty { margin: var(--space-md) 0 0; color: var(--muted); font-size: var(--text-meta-size); }
.admin-chart-details { margin-top: var(--space-md); border-top: var(--panel-border); padding-top: var(--space-sm); }
.admin-chart-details summary { cursor: pointer; color: var(--muted); font-size: var(--text-meta-size); min-height: var(--control-height-compact); align-content: center; }
.admin-chart-table-scroll { overflow-x: auto; margin-top: var(--space-sm); }
.admin-chart-panel table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.admin-chart-panel th, .admin-chart-panel td { border-bottom: 1px solid color-mix(in srgb, currentColor 22%, transparent); padding: .45rem .25rem; text-align: left; }
.admin-chart-panel th { font-size: var(--text-label-size); color: var(--muted); font-weight: 500; }
.admin-chart-panel td { font-size: var(--text-compact-size); }
.admin-skeleton-chart { width: 100%; }
.admin-skeleton-visitor-chart { aspect-ratio: 720 / 300; }
.admin-skeleton-product-chart { aspect-ratio: 720 / 186; }
.admin-skeleton-chart > .admin-skeleton-block { width: 100%; height: 100%; }
@media (max-width: 700px) {
  .admin-chart-panel { padding: var(--panel-padding-compact); }
}
@media (max-width: 700px) {
  #admin-status { --admin-status-lines: 2; }
  .admin-analytics-meta { min-height: 34px; min-height: 2lh; }
  .admin-d1-lede { min-height: 57px; min-height: 3lh; }
  /* en wraps the note to five lines in this column (measured 94.22); ko needs
     four. One floor for both states in both locales. */
  .admin-d1-note { min-height: 95px; min-height: 5lh; }
  /* Two lines per item is the measured en maximum at 360. */
  .admin-d1-account { --admin-d1-account-lines: 2; min-height: 76px; min-height: 4lh; }
}
.dashboard-card dl { display: grid; grid-template-columns: minmax(9rem, 16rem) 1fr; gap: .4rem 1rem; }
.dashboard-card dt { font-weight: 600; }
.dashboard-card dd { margin: 0; overflow-wrap: anywhere; }
.admin-access-list { display: grid; gap: .75rem; padding: 0; list-style: none; }
.admin-access-list > li { border: var(--panel-border); border-radius: var(--panel-radius); padding: var(--panel-padding-compact); overflow-wrap: anywhere; }
.admin-access-list button { margin-left: .75rem; }
.admin-inline-form { display: flex; gap: .5rem; align-items: end; }
.admin-inline-form input { min-width: min(100%, 22rem); }
#admin-access-grant, .admin-revoke-form { display: grid; gap: .55rem; max-width: 36rem; margin-top: 1rem; }
#admin-access-grant[hidden] { display: none; }
#admin-access-grant textarea, .admin-revoke-form textarea { min-height: 5rem; resize: vertical; }
.admin-confirm { display: flex; gap: .5rem; align-items: flex-start; }
.admin-confirm input { margin-top: .2rem; }
.account-page .page-shell { padding: 32px 24px; }
@media (max-width: 700px) { .admin-layout { display: block; } .admin-layout > .admin-shell { padding: 24px 16px; } .admin-sidebar { padding: 1rem; } .admin-sidebar nav { display: flex; flex-wrap: wrap; } .dashboard-grid { grid-template-columns: 1fr; } .dashboard-card dl { grid-template-columns: 1fr; } .admin-inline-form { align-items: stretch; flex-direction: column; } .admin-access-list button { display: block; margin: .65rem 0 0; width: 100%; } }
@media (max-width: 820px) {
  .account-page .page-shell { padding: 24px 16px; }
  .account-page .header-inner, .admin-page .header-inner { flex-wrap: nowrap; }
  .account-page .brand, .admin-page .brand { margin-right: 12px; }
  /* These links are duplicated by the compact menu and otherwise force the
     enlarged touch controls outside tablet and phone viewports. */
  .account-page .primary-links, .admin-page .primary-links { display: none; }
}

/* ---------------------------------------------------------------------------
   Sign-in screen.

   account.js publishes the request lifecycle on #account-status as
   `data-state`, so this file reads that one attribute to decide the layout
   (one centred column while signed out, card + request list once a session
   exists) instead of adding a second source of truth to the controller.
   --------------------------------------------------------------------------- */
.account-page .page-shell { background: none; box-shadow: none; }
.auth-layout { display: grid; gap: 20px; max-width: 27rem; margin: clamp(24px, 6vh, 64px) auto; }
.auth-layout:has(#account-status[data-state="authenticated"]) {
  max-width: 58rem;
  grid-template-columns: minmax(0, 21rem) minmax(0, 1fr);
  align-items: start;
}
.auth-card, .auth-panel {
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  padding: var(--panel-padding-roomy);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(23 32 25 / .05), 0 12px 32px rgb(23 32 25 / .07);
}
.auth-card { text-align: center; }
.auth-mark {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0 auto 16px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font: 800 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em;
}
.auth-card h1 { margin: 0; font-size: var(--title-task); line-height: 1.2; letter-spacing: -.02em; }
.auth-lede { margin: 10px 0 0; color: var(--muted); font-size: 13px; line-height: 1.55; }
.auth-status { margin: 18px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.auth-status[data-state="configuration"],
.auth-status[data-state="error"],
.auth-status[data-state="forbidden"] {
  border-radius: 8px;
  padding: 10px 12px;
  text-align: left;
}
.auth-status[data-state="configuration"] { border: 1px solid var(--state-warning); background: var(--paper); color: var(--ink); }
.auth-status[data-state="error"] { border: 1px solid var(--state-error-border); background: var(--state-error-surface); color: var(--state-error-text); }
.auth-status[data-state="forbidden"] { border: 1px solid var(--state-error-border); background: var(--state-error-surface); color: var(--state-error-text); }
/* The card already says "signed in" through the profile block below it. */
.auth-status[data-state="authenticated"] { display: none; }

/* Google's sign-in button: their mark, neutral surface, no product colour of
   ours layered on top of it. */
.google-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: var(--control-height);
  margin-top: 18px;
  border: 1px solid #d7dade;
  border-radius: var(--radius-control);
  padding: 0 16px;
  /* Google 브랜드 예외: 구글 사인인 버튼은 흰 표면+고정 글자색이 브랜드 규격이다(테스트 OHF-054).
     다른 요소와 달리 테마 토큰을 쓰지 않는다. */
  background: #fff;
  color: #1f1f1f;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.google-button:hover { border-color: #c6cace; background: #f7f8f8; box-shadow: 0 1px 3px rgb(0 0 0 / .12); }
.google-button[hidden] { display: none; }
.google-button-mark { flex: none; }
.auth-fineprint { margin: 14px 0 0; color: var(--muted); font-size: 12px; line-height: 1.5; }

.auth-profile { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--line); text-align: left; }
.auth-identity { display: flex; align-items: center; gap: 12px; }
.auth-avatar {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--soft-orange);
  color: var(--orange-dark);
  -webkit-mask: none;
  mask: none;
}
.auth-avatar::before {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background-color: currentColor;
  -webkit-mask: var(--icon-user) center / 22px 22px no-repeat;
  mask: var(--icon-user) center / 22px 22px no-repeat;
}
#account-identity { margin: 0; font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.auth-admin-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  color: var(--orange-dark);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
}
.auth-admin-link[hidden] { display: none; }
.auth-admin-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.auth-admin-link::before {
  content: "";
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: var(--icon-shield) center / contain no-repeat;
  mask: var(--icon-shield) center / contain no-repeat;
}
.auth-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.account-personalization-links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 16px; font-size: 13px; }
.cli-connections { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--line); overflow-wrap: anywhere; }
.cli-connections[hidden] { display: none; }
.cli-connections h2 { margin: 0; font-size: 16px; }
.cli-connections h3 { margin: 0 0 8px; font-size: 14px; }
.cli-connections p { margin: 8px 0; font-size: 13px; line-height: 1.6; }
.cli-connections .cli-help { color: var(--muted); }
.cli-approval { margin: 16px 0; padding: 16px; background: var(--soft-orange); border: 1px solid var(--line); border-radius: var(--radius-control); }
.cli-confirm { display: flex; align-items: flex-start; gap: 8px; margin: 14px 0; font-size: 13px; line-height: 1.6; cursor: pointer; }
.cli-confirm input { flex: none; margin-top: 4px; width: 18px; height: 18px; }
.cli-error { font-weight: 600; }
.cli-connections-list { display: grid; gap: 12px; margin: 12px 0 0; padding: 0; list-style: none; }
.cli-connections-list li { padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-control); font-size: 13px; }
.cli-connection-state { display: inline-block; margin-inline-start: 8px; color: var(--muted); }
.cli-connections .auth-button { max-width: 100%; height: auto; padding-block: 8px; white-space: normal; overflow-wrap: anywhere; }
.cli-connections .auth-button:disabled { opacity: .55; cursor: not-allowed; }
/* Account sub-menus: one link per panel, the current one marked the way the
   admin sidebar marks its active section. */
.account-sections { display: grid; gap: 4px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }
.account-sections a {
  display: flex;
  align-items: center;
  min-height: var(--control-height-default);
  padding: 0 .7rem;
  border-left: 2px solid transparent;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
.account-sections a:hover,
.account-sections a:focus-visible,
.account-sections a[aria-current="page"] { border-color: currentColor; background: color-mix(in srgb, currentColor 8%, transparent); }
.auth-panel[hidden] { display: none; }
.auth-panel .cli-connections { margin-top: 0; padding-top: 0; border-top: 0; }
.auth-button {
  min-height: var(--control-height-default);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-control);
  padding: 0 14px;
  background: var(--control-surface);
  color: var(--control-text);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}
.auth-button:hover { border-color: var(--control-border-hover); background: var(--control-surface-hover); }
.auth-button-quiet { border-color: transparent; background: none; color: var(--muted); }
.auth-button-quiet:hover { border-color: var(--line); background: var(--soft-orange); color: var(--ink); }
.auth-button[hidden] { display: none; }

/* Requests only exist for a signed-in visitor; showing an empty panel next to
   the sign-in prompt would read as a broken page. */
.auth-layout:not(:has(#account-status[data-state="authenticated"])) .auth-panel { display: none; }
.auth-panel h2 { margin: 0 0 4px; font-size: 16px; letter-spacing: -.01em; }
.auth-panel-status { margin: 0; color: var(--muted); font-size: 13px; }
.auth-panel-status:empty { display: none; }
.auth-case-list { display: grid; gap: 8px; margin: 14px 0 0; padding: 0; list-style: none; }
.auth-case-list:empty { display: none; }
.auth-case-list button {
  width: 100%;
  min-height: var(--control-height);
  border: 1px solid var(--line);
  border-radius: var(--radius-panel);
  padding: 10px 12px;
  background: var(--surface);
  color: var(--ink);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
}
.auth-case-list button:hover { border-color: var(--border-hover); background: var(--soft-orange); }
.auth-case-detail:not(:empty) {
  margin-top: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-panel);
  padding: 12px 14px;
  background: var(--paper);
  font-size: 13px;
  line-height: 1.55;
}
.auth-case-detail p { margin: 0 0 8px; overflow-wrap: anywhere; }
.auth-case-detail p:last-child { margin-bottom: 0; }

@media (max-width: 700px) {
  .auth-layout, .auth-layout:has(#account-status[data-state="authenticated"]) {
    max-width: 27rem;
    grid-template-columns: minmax(0, 1fr);
  }
  .auth-card, .auth-panel { padding: var(--panel-padding-roomy) var(--panel-padding); }
}

/* Language choice in the account/admin shell reads as one two-option control
   rather than two look-alike nav links. The active side comes from <html lang>,
   which lang-boot.js has already resolved before first paint. */
.primary-links a[href^="?lang="] {
  min-height: 32px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 12px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
.primary-links a[href^="?lang="] + a[href^="?lang="] { margin-left: 4px; }
.primary-links a[href^="?lang="]:hover {
  border-color: var(--border-hover);
  background: var(--soft-orange);
  color: var(--orange-dark);
  text-decoration: none;
}
html[lang="en"] .primary-links a[href="?lang=en"],
html[lang="ko"] .primary-links a[href="?lang=ko"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

/* Signed in, the card stops being a sign-in prompt: the pitch and the
   method note go away and the remaining identity block reads left-aligned. */
.auth-layout:has(#account-status[data-state="authenticated"]) .auth-lede,
.auth-layout:has(#account-status[data-state="authenticated"]) .auth-fineprint { display: none; }
.auth-layout:has(#account-status[data-state="authenticated"]) .auth-card { text-align: left; }
.auth-layout:has(#account-status[data-state="authenticated"]) .auth-mark { margin-inline: 0 0; }
.auth-layout:has(#account-status[data-state="authenticated"]) .auth-profile { margin-top: 18px; padding-top: 0; border-top: 0; }

/* Registration entry point. It sits under the sign-in button, where visitors
   look for it after the header sends them here with a single Log in action.
   It is shown only while the account is actually reachable and signed out —
   never over a configuration error or a live session. */
.auth-signup, .auth-signup-note { display: none; }
.auth-layout:has(#account-status[data-state="anonymous"]) .auth-signup { display: block; }
.auth-layout:has(#account-status[data-state="anonymous"]) .auth-signup-note { display: block; }
.auth-signup {
  margin: 18px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 13px;
}
.auth-signup-link {
  margin-left: 4px;
  color: var(--orange-dark);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.auth-signup-link:hover { color: var(--ink); }
.auth-signup-note { margin: 8px 0 0; color: var(--muted); font-size: 12px; line-height: 1.5; }

/* Admin markup and generated queue actions share one form-control contract.
   Checkboxes retain their native compact geometry; Google sign-in remains in
   its own branded rule above rather than inheriting product action colours. */
.admin-page :where(button, input:not([type="checkbox"]), select, textarea) {
  min-height: var(--control-height-default);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-control);
  padding: 8px 12px;
  background: var(--control-surface);
  color: var(--control-text);
  font: inherit;
}
.admin-page :where(button, input, select, textarea):not(:disabled):hover {
  border-color: var(--control-border-hover);
}
.admin-page button:not(:disabled):hover { background: var(--control-surface-hover); }
.admin-page :where(button, input, select, textarea):disabled {
  opacity: var(--control-disabled-opacity);
  cursor: not-allowed;
}
.admin-sidebar a { display: flex; align-items: center; min-height: var(--control-height-default); }
.admin-sidebar a:hover, .admin-sidebar a:focus-visible, .admin-sidebar a.is-active {
  border-color: var(--control-border-selected);
  background: var(--control-surface-selected);
  color: var(--control-text-selected);
}

@media (max-width: 820px), (pointer: coarse) {
  .auth-button,
  .auth-admin-link,
  .auth-signup-link,
  .auth-case-list button,
  .admin-sidebar a,
  .admin-page :where(button, input:not([type="checkbox"]), select, textarea) {
    min-height: var(--control-height-touch);
  }
}

.admin-sidebar a[hidden] { display: none; }
.admin-kpi { min-width: 0; }
.admin-kpi h2 { margin: 0 0 8px; font-size: 23px; letter-spacing: -.025em; }
.admin-kpi-intro, .admin-kpi-status, .admin-kpi-note { color: var(--muted); font-size: 13px; }
.admin-kpi-range { display: flex; flex-wrap: wrap; gap: 8px; border: 0; margin: 24px 0 12px; padding: 0; }
.admin-kpi-range legend { font-size: 12px; font-weight: 700; margin-bottom: 8px; }
.admin-kpi-range button[aria-pressed="true"] { background: var(--control-surface-selected); color: var(--control-text-selected); border-color: var(--control-border-selected); font-weight: 700; }
.admin-kpi-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 20px 0 24px; }
.admin-kpi-card { background: var(--surface, #fffefa); border: 1px solid var(--line); border-radius: 10px; padding: 18px; }
.admin-kpi-card h3 { margin: 0 0 12px; font-size: 13px; font-weight: 600; color: var(--muted); }
.admin-kpi-card strong { font-size: 32px; line-height: 1.15; letter-spacing: -.035em; font-variant-numeric: tabular-nums; }
.admin-kpi-card p { margin: 12px 0 0; font-size: 11px; color: var(--muted); line-height: 1.6; }
.admin-kpi-charts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.admin-kpi-chart { min-width: 0; margin: 0; padding: 18px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface, #fffefa); }
.admin-kpi-chart figcaption { margin: 0 8px 18px; font-size: 15px; font-weight: 750; }
.admin-kpi-chart svg { display: block; width: 100%; height: auto; overflow: visible; }
.kpi-gridline { stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 4; }
.kpi-axis-label { fill: var(--muted); font-size: 12px; }
.kpi-registration-bar { fill: var(--chart-series-1); }
.kpi-line { fill: none; stroke-width: 2.5; stroke-linejoin: round; }
.kpi-series-0 { stroke: var(--chart-series-0); fill: var(--chart-series-0); }
.kpi-series-1 { stroke: var(--chart-series-1); fill: var(--chart-series-1); stroke-dasharray: 6 3; }
.kpi-series-2 { stroke: var(--chart-series-2); fill: var(--chart-series-2); stroke-dasharray: 2 4; }
.kpi-line.kpi-series-0, .kpi-line.kpi-series-1, .kpi-line.kpi-series-2 { fill: none; }
.admin-kpi-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; list-style: none; padding: 0 8px; font-size: 11px; }
.admin-kpi-legend li::before { content: ""; display: inline-block; width: 16px; border-top: 3px solid var(--chart-series-0); margin-right: 6px; vertical-align: middle; }
.admin-kpi-legend .kpi-legend-1::before { border-top: 3px dashed var(--chart-series-1); }
.admin-kpi-legend .kpi-legend-2::before { border-top: 3px dotted var(--chart-series-2); }
.admin-kpi-notes { border-left: 3px solid var(--line); padding-left: 14px; margin: 22px 0; color: var(--muted); font-size: 12px; line-height: 1.6; }
.admin-kpi-notes p { margin: 6px 0; overflow-wrap: anywhere; }
.admin-kpi-data summary { cursor: pointer; font-size: 13px; font-weight: 600; padding: 12px 0; }
.admin-kpi-table-scroll { overflow-x: auto; }
.admin-kpi-data table { border-collapse: collapse; width: 100%; font-size: 12px; font-variant-numeric: tabular-nums; }
.admin-kpi-data th, .admin-kpi-data td { padding: 10px; border-bottom: 1px solid var(--line); text-align: right; white-space: nowrap; }
.admin-kpi-data th:first-child { text-align: left; }
.admin-kpi-data caption { text-align: left; color: var(--muted); padding: 8px 0; }
@media (max-width: 1080px) { .admin-kpi-charts { grid-template-columns: 1fr; } }
@media (max-width: 560px) { .admin-kpi-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; } .admin-kpi-card { padding: 14px 12px; } .admin-kpi-card strong { font-size: 27px; } }

/* Task panels own their loading and result context; keep global auth errors visible. */
.admin-shell:has(#admin-support:not([hidden]), #admin-statistics:not([hidden]), #admin-access:not([hidden])) > .lede { display: none; }
.admin-shell:has(#admin-support:not([hidden]), #admin-statistics:not([hidden]), #admin-access:not([hidden])) > .admin-notice { padding: 0; border: 0; margin: 0; background: none; }
.admin-shell:has(#admin-support:not([hidden]), #admin-statistics:not([hidden]), #admin-access:not([hidden])) #admin-status:is([data-state="populated"], [data-state="loading"]) { display: none; }
#admin-support > h2 { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Keep menu navigation headings below the sticky site header. */
#admin-section-title { scroll-margin-top: 110px; }
.auth-case-detail h3 { margin: 0 0 12px; font-size: 15px; }
.auth-case-detail h4 { margin: 16px 0 8px; font-size: 13px; }
.auth-case-detail dl { display: grid; gap: 6px; margin: 0; color: var(--muted); }
.auth-case-detail dl > div { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.auth-case-detail dt { font-weight: 600; }
.auth-case-detail dd { margin: 0; overflow-wrap: anywhere; }
.auth-case-detail ol { padding-left: 20px; }
.auth-case-detail li + li { margin-top: 12px; }
.auth-case-detail section p { white-space: pre-wrap; }
.admin-screen-reader { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; }
.admin-users { min-width: 0; }
.admin-users #admin-access-status:empty { display: none; }
.admin-users-filters { display: grid; grid-template-columns: minmax(180px, 1fr) minmax(130px, 180px) auto; gap: 8px; align-items: end; margin: 0 0 18px; }
.admin-users-filters label { display: flex; flex-direction: column; gap: 6px; font-size: 12px; font-weight: 700; color: var(--muted); }
.admin-users-filters input, .admin-users-filters select { width: 100%; margin: 0; height: 44px; min-height: 44px; }
.admin-users-filters > button { height: 44px; }
.admin-users-workspace { display: grid; grid-template-columns: minmax(260px, .86fr) minmax(0, 1.4fr); gap: 16px; align-items: start; }
.admin-users-list-pane, .admin-users-detail { min-width: 0; }
.admin-users #admin-access-users { max-height: 520px; overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; margin: 0; gap: 6px; }
.admin-users #admin-access-users > li { padding: 0; border: 0; }
.admin-users .admin-user-row { width: 100%; display: block; text-align: left; margin: 0; padding: 12px; }
.admin-users .admin-user-row[aria-pressed="true"] { border-color: var(--control-border-selected); background: var(--control-surface-selected); }
.admin-users .admin-user-row strong, .admin-users .admin-user-row span { display: block; }
.admin-users .admin-user-row span { color: var(--muted); font-size: 12px; margin-top: 4px; }
.admin-user-row span { overflow-wrap: anywhere; }
.admin-users-detail { border: 1px solid var(--line); border-radius: var(--panel-radius, 10px); background: var(--surface, #fffefa); padding: 16px; }
.admin-users-detail h3 { margin-top: 0; scroll-margin-top: 110px; }
.admin-users .admin-confirm input { width: 20px; height: 20px; min-height: 20px; flex: 0 0 20px; margin: 2px 0 0; }
.admin-users #admin-access-grant input:not([type="checkbox"]) { width: 100%; margin: 0; }
.admin-users #admin-access-grant .admin-confirm { margin: 8px 0; }
.admin-user-detail { display: grid; grid-template-columns: minmax(7rem, 12rem) 1fr; gap: 8px 12px; }
.admin-user-detail dt { font-weight: 700; color: var(--muted); }
.admin-user-detail dd { margin: 0; overflow-wrap: anywhere; }
.admin-users-more { width: 100%; margin-top: 10px; }
.admin-users-history { margin-top: 18px; }
@media (max-width: 980px) { .admin-users-workspace, .admin-users-filters { grid-template-columns: 1fr; } .admin-user-detail { grid-template-columns: 1fr; } .admin-users #admin-access-users { max-height: 320px; } }
.benchmark-admin-form, .benchmark-admin-actions { display: flex; align-items: end; flex-wrap: wrap; gap: 1rem; margin: 1rem 0; }
.benchmark-admin-form label, .benchmark-admin-actions label, .benchmark-admin-select { display: grid; gap: .4rem; }
#admin-benchmark input, #admin-benchmark select { max-width: 100%; min-width: 0; padding: .65rem; }
#benchmark-admin-task, #benchmark-admin-preset { overflow-wrap: anywhere; }
#benchmark-admin-counts { display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; list-style: none; padding: 0; }

/* ADR-0035 catalog exclusions */
.admin-exclusions-form { display: grid; gap: .75rem; max-width: 40rem; margin: 1rem 0 1.5rem; }
.admin-exclusions-form > label:not(.admin-confirm), .admin-exclusions-revoke label { display: grid; gap: .4rem; }
.admin-exclusions-form input:not([type="checkbox"]), .admin-exclusions-form select, .admin-exclusions-form textarea, .admin-exclusions-revoke textarea { box-sizing: border-box; width: 100%; max-width: 100%; margin: 0; }
.admin-exclusions-form textarea, .admin-exclusions-revoke textarea { resize: vertical; }
.admin-exclusions-list { list-style: none; padding: 0; margin: .5rem 0 1.5rem; display: grid; gap: .75rem; }
.admin-exclusions-item { padding: .75rem 1rem; border: 1px solid var(--line); border-radius: 8px; overflow-wrap: anywhere; }
.admin-exclusions-item p { margin: .35rem 0; }
.admin-exclusions-meta, .admin-exclusions-matched { color: var(--muted, inherit); font-size: .9em; }
.admin-exclusions-revoke { display: grid; gap: .5rem; margin-top: .5rem; }
.admin-exclusions .admin-confirm input { width: 20px; height: 20px; min-height: 20px; flex: 0 0 20px; margin: 2px 0 0; }
