/* ==========================================================================
   oodot design system — components.css
   tokens.css 를 먼저 불러온 뒤에 이 파일을 불러온다.
   모든 클래스는 od- 로 시작한다. 서비스 쪽 클래스와 부딪히지 않게.
   v1.1.1
   ========================================================================== */

/* ==========================================================================
   타이포 유틸
   ========================================================================== */
.od-display-1 { font-size: var(--od-size-display-1); line-height: var(--od-leading-display-1); letter-spacing: var(--od-tracking-display-1); font-weight: var(--od-weight-semibold); margin: 0; }
.od-display-2 { font-size: var(--od-size-display-2); line-height: var(--od-leading-display-2); letter-spacing: var(--od-tracking-display-2); font-weight: var(--od-weight-semibold); margin: 0; }
.od-title-1   { font-size: var(--od-size-title-1);   line-height: var(--od-leading-title-1);   letter-spacing: var(--od-tracking-title-1);   font-weight: var(--od-weight-semibold); margin: 0; }
.od-title-2   { font-size: var(--od-size-title-2);   line-height: var(--od-leading-title-2);   letter-spacing: var(--od-tracking-title-2);   font-weight: var(--od-weight-semibold); margin: 0; }
.od-title-3   { font-size: var(--od-size-title-3);   line-height: var(--od-leading-title-3);   letter-spacing: var(--od-tracking-title-3);   font-weight: var(--od-weight-semibold); margin: 0; }
.od-headline  { font-size: var(--od-size-headline);  line-height: var(--od-leading-headline);  letter-spacing: var(--od-tracking-headline);  font-weight: var(--od-weight-semibold); margin: 0; }
.od-body      { font-size: var(--od-size-body);      line-height: var(--od-leading-body);      letter-spacing: var(--od-tracking-body);      margin: 0; }
.od-subhead   { font-size: var(--od-size-subhead);   line-height: var(--od-leading-subhead);   letter-spacing: var(--od-tracking-subhead);   margin: 0; }
.od-footnote  { font-size: var(--od-size-footnote);  line-height: var(--od-leading-footnote);  letter-spacing: var(--od-tracking-footnote);  margin: 0; }
.od-caption   { font-size: var(--od-size-caption);   line-height: var(--od-leading-caption);   letter-spacing: var(--od-tracking-caption);   margin: 0; }

.od-text-secondary { color: var(--od-text-secondary); }
.od-text-tertiary  { color: var(--od-text-tertiary); }
.od-text-accent    { color: var(--od-text-accent); }
.od-text-success   { color: var(--od-text-success); }
.od-text-warning   { color: var(--od-text-warning); }
.od-text-danger    { color: var(--od-text-danger); }
.od-mono           { font-family: var(--od-font-mono); font-size: .9em; letter-spacing: 0; }

/* 화면에는 없고 보조기기에만 읽히는 글 */
.od-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ==========================================================================
   Link
   ========================================================================== */
/* 주소로 가는 링크(<a>)는 늘 옅은 밑줄 — 파랑과 둘레 글자의 밝기 차가 작아(회색 글 속에선 1.3:1) 색만으론 못 알아본다.
   밑줄은 글자색 35% 로 얇게 두고 올리면 진해진다. 같은 모양의 <button>(비밀번호 찾기·코드 다시 받기)은
   홀로 서 있는 동작이라 긋지 않는다. --quiet 는 목록 제목처럼 자리로 링크임이 드러나는 곳에만.
   바닥글 링크는 이 규칙을 따르지 않는다(밑줄 없음, 올리면 밑줄). */
.od-link {
  color: var(--od-text-accent);
  text-decoration: none;
  border-radius: var(--od-radius-xs);
  transition: color var(--od-duration-fast) var(--od-ease-inout), text-decoration-color var(--od-duration-fast) var(--od-ease-inout);
}
a.od-link {
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: currentColor;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
}
.od-link:hover { color: var(--od-blue-700); text-decoration-line: underline; text-underline-offset: 3px; text-decoration-color: currentColor; }
.od-link--quiet, a.od-link--quiet { color: var(--od-text); text-decoration-line: none; }
.od-link--quiet:hover { color: var(--od-text-accent); text-decoration-line: underline; }

/* ==========================================================================
   Button
   변형 4 (primary · secondary · plain · danger) × 크기 3 (sm · md · lg)
   ========================================================================== */
.od-btn {
  --_bg: transparent;
  --_fg: var(--od-text);
  --_border: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--od-space-2);
  height: var(--od-control-height-md);
  padding: 0 var(--od-space-4);
  border: 1px solid var(--_border);
  border-radius: var(--od-radius-md);
  background: var(--_bg);
  color: var(--_fg);
  font-size: var(--od-size-subhead);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-subhead);
  line-height: 1;
  white-space: nowrap;
  /* <a> 로 만든 버튼에 밑줄이 그어지지 않게 */
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--od-duration-fast) var(--od-ease-inout),
    border-color var(--od-duration-fast) var(--od-ease-inout),
    color var(--od-duration-fast) var(--od-ease-inout),
    transform var(--od-duration-instant) var(--od-ease-out);
}
/* 누르는 즉시 반응한다. 떼는 순간이 아니라. */
.od-btn:active:not(:disabled) { transform: scale(.97); }
.od-btn:disabled,
.od-btn[aria-disabled="true"] { cursor: not-allowed; opacity: .4; }

.od-btn--sm { position: relative; height: var(--od-control-height-sm); padding: 0 var(--od-space-3); font-size: var(--od-size-footnote); border-radius: var(--od-radius-sm); gap: var(--od-space-1); }
/* 28 → 44. 세로만 넓힌다 — 가로까지 넓히면 gap 8 로 붙은 버튼끼리 눌리는 범위가 겹친다. */
.od-btn--sm::before { content: ""; position: absolute; inset: -8px 0; }
.od-btn--icon.od-btn--sm::before { inset: -8px; }
.od-btn--lg { height: var(--od-control-height-lg); padding: 0 var(--od-space-6); font-size: var(--od-size-body); border-radius: var(--od-radius-md); }

.od-btn--primary   { --_bg: var(--od-accent); --_fg: var(--od-text-on-accent); }
.od-btn--primary:hover:not(:disabled)  { --_bg: var(--od-accent-hover); }
.od-btn--primary:active:not(:disabled) { --_bg: var(--od-accent-active); }

.od-btn--secondary { --_bg: var(--od-bg); --_fg: var(--od-text); --_border: var(--od-border-strong); }
.od-btn--secondary:hover:not(:disabled)  { --_bg: var(--od-gray-25); }
.od-btn--secondary:active:not(:disabled) { --_bg: var(--od-gray-50); }

.od-btn--plain { --_fg: var(--od-text); }
.od-btn--plain:hover:not(:disabled)  { --_bg: var(--od-bg-hover); }
.od-btn--plain:active:not(:disabled) { --_bg: var(--od-bg-active); }

.od-btn--danger { --_bg: var(--od-danger); --_fg: var(--od-text-on-accent); }
.od-btn--danger:hover:not(:disabled)  { --_bg: var(--od-danger-hover); }

/* 아이콘만 있는 버튼 — 정사각 */
.od-btn--icon { width: var(--od-control-height-md); padding: 0; }
.od-btn--icon.od-btn--sm { width: var(--od-control-height-sm); }
.od-btn--icon.od-btn--lg { width: var(--od-control-height-lg); }

/* 기다리는 중 — 글자는 남기고 자리만 지킨다 */
.od-btn[data-loading="true"] { color: transparent; pointer-events: none; position: relative; }
.od-btn[data-loading="true"]::after {
  content: ""; position: absolute;
  width: 1em; height: 1em;
  border: 2px solid currentColor;
  border-color: color-mix(in srgb, var(--_fg) 100%, transparent) transparent transparent transparent;
  color: var(--_fg);
  border-radius: 50%;
  animation: od-spin .7s linear infinite;
}
@keyframes od-spin { to { transform: rotate(1turn); } }

.od-btn-group { display: inline-flex; gap: var(--od-space-2); flex-wrap: wrap; }

/* ==========================================================================
   Badge · Chip
   ========================================================================== */
.od-badge {
  --_bg: var(--od-gray-100);
  --_fg: var(--od-text-secondary);
  display: inline-flex; align-items: center; gap: var(--od-space-1);
  height: 22px; padding: 0 var(--od-space-2);
  border-radius: var(--od-radius-xs);
  background: var(--_bg); color: var(--_fg);
  font-size: var(--od-size-caption);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-caption);
  line-height: 1; white-space: nowrap;
}
.od-badge--sm { height: 18px; padding: 0 var(--od-space-1); font-size: var(--od-size-caption); }
.od-badge--accent  { --_bg: var(--od-accent-subtle);  --_fg: var(--od-text-accent); }
.od-badge--success { --_bg: var(--od-success-subtle); --_fg: var(--od-text-success); }
.od-badge--warning { --_bg: var(--od-warning-subtle); --_fg: var(--od-text-warning); }
.od-badge--danger  { --_bg: var(--od-danger-subtle);  --_fg: var(--od-text-danger); }
.od-badge--solid   { --_bg: var(--od-gray-900); --_fg: var(--od-text-on-accent); }
.od-badge--dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* 숫자만 담는 알약 */
.od-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--od-radius-full);
  background: var(--od-gray-900); color: var(--od-text-on-accent);   /* 세는 것이지 누르는 것이 아니다 — 파랑 아님 */
  font-size: var(--od-size-caption); font-weight: var(--od-weight-semibold); line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* 지울 수 있는 표식 */
.od-chip {
  display: inline-flex; align-items: center; gap: var(--od-space-2);
  height: 28px; padding: 0 var(--od-space-1) 0 var(--od-space-3);
  border: 1px solid var(--od-border);
  border-radius: var(--od-radius-full);
  background: var(--od-bg);
  font-size: var(--od-size-footnote);
  transition: border-color var(--od-duration-fast) var(--od-ease-inout),
              background-color var(--od-duration-fast) var(--od-ease-inout);
}
.od-chip--plain { padding-right: var(--od-space-3); }
.od-chip__remove {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  width: 20px; height: 20px; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--od-text-tertiary);
  cursor: pointer;
}
/* 칩이 28 높이라 여기서 44 를 만들면 칩 밖으로 새어 칩 본체의 탭을 훔친다.
   칩 높이가 허락하는 만큼(28×32)까지만 넓힌다. 진짜 44 가 필요하면 칩을 lg 로 만들 것. */
.od-chip__remove::before { content: ""; position: absolute; inset: -4px -6px; }
.od-chip__remove:hover { background: var(--od-bg-active); color: var(--od-text); }
.od-chip[aria-pressed="true"],
.od-chip--selected { border-color: var(--od-gray-900); background: var(--od-gray-900); color: var(--od-text-on-accent); }

/* ==========================================================================
   Form
   ========================================================================== */
.od-field { display: flex; flex-direction: column; gap: var(--od-space-2); }
.od-label {
  font-size: var(--od-size-subhead);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-subhead);
}
.od-label__optional { color: var(--od-text-tertiary); font-weight: var(--od-weight-regular); }
.od-help  { font-size: var(--od-size-footnote); color: var(--od-text-secondary); }
/* 비어 있으면 스스로 접힌다 — hidden 으로 숨기면 살아 있는 영역이 아니라서
   화면을 읽어 주는 프로그램이 오류를 읽어 주지 않는다. role="alert" 와 한 쌍이다. */
.od-error { font-size: var(--od-size-footnote); color: var(--od-text-danger); display: flex; align-items: flex-start; gap: var(--od-space-1); }
.od-error:empty { display: none; }
/* 아이콘은 첫 줄 가운데에 — 오류가 두 줄이 되어도 두 줄 사이에 걸리지 않는다 */
.od-error > svg { flex: none; margin-top: 3px; margin-top: calc((1lh - 12px) / 2); }

.od-input, .od-textarea, .od-select {
  width: 100%;
  min-height: var(--od-control-height-md);
  padding: var(--od-space-2) var(--od-space-3);
  border: 1px solid var(--od-border-strong);
  border-radius: var(--od-radius-md);
  background: var(--od-bg);
  color: var(--od-text);
  font-size: var(--od-size-subhead);
  letter-spacing: var(--od-tracking-subhead);
  transition: border-color var(--od-duration-fast) var(--od-ease-inout),
              box-shadow var(--od-duration-fast) var(--od-ease-inout);
}
.od-input::placeholder, .od-textarea::placeholder { color: var(--od-text-tertiary); }
.od-input:hover:not(:disabled), .od-textarea:hover:not(:disabled), .od-select:hover:not(:disabled) { border-color: var(--od-gray-500); }
.od-input:focus, .od-textarea:focus, .od-select:focus {
  outline: none;
  border-color: var(--od-accent);
  box-shadow: var(--od-shadow-focus);
}
.od-input:disabled, .od-textarea:disabled, .od-select:disabled {
  background: var(--od-bg-disabled); color: var(--od-text-disabled); cursor: not-allowed;
}
.od-input[aria-invalid="true"], .od-textarea[aria-invalid="true"] { border-color: var(--od-danger); }
/* 틀린 칸은 초점이 가도 틀린 칸이다 — 테두리까지 같이 빨갛게 두지 않으면
   초점이 가는 순간 파래져서 고쳐진 것처럼 보인다. */
.od-input[aria-invalid="true"]:focus, .od-textarea[aria-invalid="true"]:focus {
  border-color: var(--od-danger);
  box-shadow: 0 0 0 3px rgba(196, 48, 43, .22);
}
.od-textarea { min-height: 96px; resize: vertical; line-height: 1.55; }
.od-input--lg { min-height: var(--od-control-height-lg); font-size: var(--od-size-body); }
.od-input--sm { min-height: var(--od-control-height-sm); font-size: var(--od-size-footnote); padding: var(--od-space-1) var(--od-space-2); }
/* 손가락 화면 — 아이폰 사파리는 16px 보다 작은 칸을 누르면 화면을 확대한다. 칸 글자만 16px 로 */
@media (pointer: coarse) {
  .od-input, .od-input--sm, .od-textarea, .od-select { font-size: var(--od-size-body); }
}

.od-select {
  appearance: none;
  padding-right: var(--od-space-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%236b6b73' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--od-space-3) center;
}

/* 앞뒤에 붙는 글 (단위, 주소 앞머리) */
.od-input-group { display: flex; align-items: stretch; }
.od-input-group > .od-input { border-radius: 0; }
.od-input-group > :first-child { border-top-left-radius: var(--od-radius-md); border-bottom-left-radius: var(--od-radius-md); }
.od-input-group > :last-child  { border-top-right-radius: var(--od-radius-md); border-bottom-right-radius: var(--od-radius-md); }
.od-input-group__addon {
  display: flex; align-items: center;
  padding: 0 var(--od-space-3);
  border: 1px solid var(--od-border-strong);
  background: var(--od-bg-subtle);
  color: var(--od-text-secondary);
  font-size: var(--od-size-subhead);
  white-space: nowrap;
}
.od-input-group__addon:first-child { border-right: 0; }
.od-input-group__addon:last-child  { border-left: 0; }

/* 비밀번호 칸 — 칸 안 오른쪽의 눈 단추로 보이기·숨기기를 켜고 끈다(누르는 동안만이 아니다).
 * 단추는 aria-pressed 와 aria-label("비밀번호 보기"/"비밀번호 숨기기")을 바꾸고 커서는 칸에 남긴다. (2026-10-07, 계정 화면에서 옮김) */
.od-password { position: relative; }
.od-password > .od-input { width: 100%; padding-right: 48px; }
.od-password__peek {
  position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
  display: grid; place-items: center;
  width: 40px; height: 40px; padding: 0;
  border: 0; border-radius: var(--od-radius-sm);
  background: none; color: var(--od-text-tertiary);
  cursor: pointer;
}
.od-password__peek:hover { color: var(--od-text); background: var(--od-bg-hover); }
.od-password__peek:focus-visible { outline: 2px solid var(--od-focus); outline-offset: -2px; }
.od-password__peek svg { display: block; }

/* 규칙 목록 — 치는 동안 맞은 줄이 체크로 바뀐다(li[data-ok="true"]). 색만이 아니라 모양도 바뀐다. */
.od-rules {
  display: flex; flex-wrap: wrap; gap: var(--od-space-1) var(--od-space-4);
  margin: 0; padding: 0; list-style: none;
  font-size: var(--od-size-footnote); color: var(--od-text-secondary);
}
.od-rules li { display: inline-flex; align-items: center; gap: 6px; }
.od-rules li::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: var(--od-border-strong);
  -webkit-mask: radial-gradient(circle, #000 2.5px, transparent 3px);
          mask: radial-gradient(circle, #000 2.5px, transparent 3px);
}
.od-rules li[data-ok="true"] { color: var(--od-text); }
.od-rules li[data-ok="true"]::before {
  background: var(--od-text);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2 5 8.6l4.5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 6.2 5 8.6l4.5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
}

/* '또는' 구분선 — 이메일로 하는 방법과 다른 방법(Google 등) 사이 */
.od-or {
  display: flex; align-items: center; gap: var(--od-space-3);
  margin: var(--od-space-6) 0 var(--od-space-4);
  font-size: var(--od-size-footnote); color: var(--od-text-tertiary);
}
.od-or::before, .od-or::after { content: ""; flex: 1; height: 1px; background: var(--od-border-subtle); }

/* 체크 · 라디오 */
.od-check { display: inline-flex; align-items: flex-start; gap: var(--od-space-2); cursor: pointer; }
.od-check input {
  appearance: none; flex: none;
  position: relative;
  width: 18px; height: 18px; margin: 1px 0 0;
  border: 1px solid var(--od-border-strong);
  background: var(--od-bg);
  cursor: pointer;
  transition: background-color var(--od-duration-fast) var(--od-ease-inout),
              border-color var(--od-duration-fast) var(--od-ease-inout);
}
/* 보이는 크기는 18 이지만 손가락이 닿는 범위는 44×44 다.
   이름표가 같은 컨트롤이라 글자 위로 겹쳐도 하는 일이 달라지지 않는다. */
.od-check input::before { content: ""; position: absolute; inset: -13px; }
.od-check input[type="checkbox"] { border-radius: var(--od-radius-xs); }
.od-check input[type="radio"]    { border-radius: 50%; }
.od-check input:checked { background-color: var(--od-accent); border-color: var(--od-accent); }
.od-check input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' viewBox='0 0 12 10'%3E%3Cpath d='M1 5.2 4.3 8.5 11 1.5' fill='none' stroke='%23fff' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center;
}
.od-check input[type="radio"]:checked {
  background-image: radial-gradient(circle, var(--od-text-on-accent) 0 3px, transparent 3.5px);
}
.od-check input:disabled { background: var(--od-bg-disabled); border-color: var(--od-border); cursor: not-allowed; }
.od-check input:disabled + span { color: var(--od-text-disabled); }
.od-check__text { font-size: var(--od-size-subhead); }

/* 스위치 — 켜고 끄는 것이 즉시 일어날 때만 */
.od-switch { display: inline-flex; align-items: center; gap: var(--od-space-3); cursor: pointer; }
.od-switch input {
  appearance: none; position: relative; flex: none;
  width: 44px; height: 26px; margin: 0;
  border-radius: var(--od-radius-full);
  background: var(--od-gray-300);
  cursor: pointer;
  transition: background-color var(--od-duration-base) var(--od-ease-inout);
}
.od-switch input::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--od-gray-0);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .2);
  transition: transform var(--od-duration-base) var(--od-ease-out);
}
.od-switch input:checked { background: var(--od-accent); }
.od-switch input:checked::after { transform: translateX(18px); }
.od-switch input:disabled { opacity: .45; cursor: not-allowed; }

/* ==========================================================================
   Segmented control — 선택지가 2~4개, 결과가 바로 보일 때
   ========================================================================== */
.od-segmented {
  display: inline-flex;
  padding: 2px;
  border-radius: var(--od-radius-md);
  background: var(--od-bg-sunken);
}
.od-segmented__item {
  padding: 0 var(--od-space-4);
  height: 30px;
  border: 0; border-radius: calc(var(--od-radius-md) - 2px);
  background: transparent;
  color: var(--od-text-secondary);
  font-size: var(--od-size-footnote);
  font-weight: var(--od-weight-medium);
  cursor: pointer;
  transition: color var(--od-duration-fast) var(--od-ease-inout);
}
/* 하나만 눌린 버튼 묶음 — <div role="group" aria-label="…"> 안에 <button aria-pressed>. 탭이 아니다(아래 패널을 바꾸지 않는다).
   aria-selected 는 옛 표기(2026-10-07 전) — 이미 가져다 쓴 곳이 깨지지 않게 같이 받는다. */
.od-segmented__item[aria-pressed="true"],
.od-segmented__item[aria-selected="true"] {
  background: var(--od-bg);
  color: var(--od-text);
  box-shadow: var(--od-shadow-1);
}

/* ==========================================================================
   Card
   ========================================================================== */
.od-card {
  display: block;
  padding: var(--od-space-6);
  border: 1px solid var(--od-border);
  border-radius: var(--od-radius-lg);
  background: var(--od-bg);
}
.od-card--raised { border-color: transparent; box-shadow: var(--od-shadow-2); }
.od-card--flush  { padding: 0; overflow: hidden; }
.od-card__header { display: flex; align-items: baseline; justify-content: space-between; gap: var(--od-space-4); margin-bottom: var(--od-space-2); }
.od-card__footer { margin-top: var(--od-space-5); padding-top: var(--od-space-4); border-top: 1px solid var(--od-border-subtle); }

a.od-card, .od-card--interactive {
  color: inherit; text-decoration: none;
  transition: border-color var(--od-duration-fast) var(--od-ease-inout),
              box-shadow var(--od-duration-base) var(--od-ease-out),
              transform var(--od-duration-base) var(--od-ease-out);
}
a.od-card:hover, .od-card--interactive:hover { border-color: var(--od-border-strong); box-shadow: var(--od-shadow-2); transform: translateY(-1px); }
a.od-card:active, .od-card--interactive:active { transform: translateY(0) scale(.995); }

/* ==========================================================================
   List · 설정 줄
   ========================================================================== */
.od-list { list-style: none; margin: 0; padding: 0; }
.od-list > li + li { border-top: 1px solid var(--od-border-subtle); }
.od-row {
  display: flex; align-items: center; gap: var(--od-space-4);
  padding: var(--od-space-3) var(--od-space-5);
  min-height: 52px;
}
.od-row__main  { flex: 1; min-width: 0; }
.od-row__title { font-size: var(--od-size-subhead); font-weight: var(--od-weight-medium); }
.od-row__desc  { font-size: var(--od-size-footnote); color: var(--od-text-secondary); margin-top: 2px; }
.od-row--link { cursor: pointer; color: inherit; text-decoration: none; transition: background-color var(--od-duration-fast) var(--od-ease-inout); }
.od-row--link:hover { background: var(--od-bg-hover); }
.od-row__chevron { flex: none; color: var(--od-text-tertiary); }

/* ==========================================================================
   Table
   ========================================================================== */
.od-table-wrap { overflow-x: auto; border: 1px solid var(--od-border); border-radius: var(--od-radius-lg); background: var(--od-bg); }
.od-table { width: 100%; border-collapse: collapse; font-size: var(--od-size-subhead); }
.od-table th, .od-table td { padding: var(--od-space-3) var(--od-space-4); text-align: left; }
.od-table thead th {
  position: sticky; top: 0;
  background: var(--od-bg-subtle);
  color: var(--od-text-secondary);
  font-size: var(--od-size-footnote);
  font-weight: var(--od-weight-medium);
  border-bottom: 1px solid var(--od-border);
  white-space: nowrap;
}
.od-table tbody tr + tr td { border-top: 1px solid var(--od-border-subtle); }
.od-table tbody tr:hover td { background: var(--od-bg-hover); }
.od-table td.od-num, .od-table th.od-num { text-align: right; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Tabs
   ========================================================================== */
.od-tabs { display: flex; gap: var(--od-space-6); border-bottom: 1px solid var(--od-border); }
.od-tab {
  position: relative;
  padding: var(--od-space-3) 0;
  border: 0; background: none;
  color: var(--od-text-secondary);
  font-size: var(--od-size-subhead);
  font-weight: var(--od-weight-medium);
  cursor: pointer;
  transition: color var(--od-duration-fast) var(--od-ease-inout);
}
.od-tab:hover { color: var(--od-text); }
.od-tab[aria-selected="true"] { color: var(--od-text); }
.od-tab[aria-selected="true"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; border-radius: 2px 2px 0 0;
  background: var(--od-text);
}

/* ==========================================================================
   Toolbar — 내용이 아래로 지나가는 반투명 층
   ========================================================================== */
.od-toolbar {
  position: sticky; top: 0; z-index: var(--od-z-sticky);
  display: flex; align-items: center; gap: var(--od-space-4);
  height: 52px; padding: 0 var(--od-space-5);
  background: rgba(255, 255, 255, .72);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--od-duration-base) var(--od-ease-inout);
}
/* 내용이 실제로 밑으로 들어갔을 때만 선이 나타난다 */
.od-toolbar[data-scrolled="true"] { border-bottom-color: var(--od-border); }
.od-toolbar__title { font-size: var(--od-size-subhead); font-weight: var(--od-weight-semibold); }
.od-toolbar__spacer { flex: 1; }

@media (prefers-reduced-transparency: reduce) {
  .od-toolbar { background: var(--od-bg); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ==========================================================================
   Callout — 화면 안에 머무는 알림
   ========================================================================== */
.od-callout {
  --_bg: var(--od-bg-subtle);
  --_border: var(--od-border);
  --_fg: var(--od-text);
  display: flex; gap: var(--od-space-3);
  padding: var(--od-space-4);
  border: 1px solid var(--_border);
  border-radius: var(--od-radius-md);
  background: var(--_bg);
  font-size: var(--od-size-subhead);
}
.od-callout__icon { flex: none; color: var(--_fg); line-height: 1.4; }
.od-callout__body { flex: 1; min-width: 0; }
.od-callout__title { font-weight: var(--od-weight-semibold); margin-bottom: 2px; }
.od-callout--info    { --_bg: var(--od-accent-subtle);  --_border: var(--od-accent-muted);   --_fg: var(--od-text-accent); }
.od-callout--success { --_bg: var(--od-success-subtle); --_border: var(--od-success-border); --_fg: var(--od-text-success); }
.od-callout--warning { --_bg: var(--od-warning-subtle); --_border: var(--od-warning-border); --_fg: var(--od-text-warning); }
.od-callout--danger  { --_bg: var(--od-danger-subtle);  --_border: var(--od-danger-border);  --_fg: var(--od-text-danger); }

/* ==========================================================================
   Scrim · Modal · Sheet
   열림/닫힘은 motion.js 의 스프링이 맡는다. 여기서는 모양만.
   ========================================================================== */
.od-scrim {
  position: fixed; inset: 0; z-index: var(--od-z-scrim);
  background: var(--od-scrim);
  opacity: 0;
  transition: opacity var(--od-duration-base) var(--od-ease-inout);
}
.od-scrim[data-open="true"] { opacity: 1; }

.od-modal {
  position: fixed; z-index: var(--od-z-modal);
  left: 50%; top: 50%;
  width: min(440px, calc(100vw - 32px));
  padding: var(--od-space-6);
  border-radius: var(--od-radius-xl);
  background: var(--od-bg);
  box-shadow: var(--od-shadow-4);
  transform: translate(-50%, -50%);
}
.od-modal__title { margin: 0 0 var(--od-space-2); font-size: var(--od-size-headline); font-weight: var(--od-weight-semibold); letter-spacing: var(--od-tracking-headline); }
.od-modal__body  { color: var(--od-text-secondary); font-size: var(--od-size-subhead); }
.od-modal__actions { display: flex; justify-content: flex-end; gap: var(--od-space-2); margin-top: var(--od-space-6); }

/* 미리보기 모달 — 옮겨 간 곳의 화면 한 조각을 위에 두고 알린다(2026-10-07, Hubb 공지 창).
 * 조각은 사진이 아니라 실제 데이터로 그린다. 회색 판 위 흰 페이지가 아래로 잘린다. */
.od-modal--peek { width: min(520px, calc(100vw - 32px)); padding: var(--od-space-3) var(--od-space-3) var(--od-space-6); }
.od-modal--peek .od-modal__text { padding: var(--od-space-5) var(--od-space-3) 0; word-break: keep-all; overflow-wrap: anywhere; }
.od-modal--peek .od-modal__title { font-size: var(--od-size-title-3, 1.25rem); }
.od-modal--peek .od-modal__body { font-size: var(--od-size-callout, .9375rem); }
.od-modal--peek .od-modal__actions { padding: 0 var(--od-space-3); }
.od-modal__peek { padding: var(--od-space-4) var(--od-space-4) 0; border-radius: var(--od-radius-lg, 10px); background: var(--od-gray-50); overflow: hidden; }
.od-modal__peek-page { padding: 0 var(--od-space-4); border: 1px solid var(--od-gray-200); border-bottom: 0; border-radius: var(--od-radius-md) var(--od-radius-md) 0 0; background: var(--od-bg); }
.od-modal__peek-bar { display: flex; align-items: center; gap: 6px; height: 2.5rem; border-bottom: 1px solid var(--od-border-subtle); font-size: var(--od-size-caption, .75rem); }
.od-modal__peek-bar b { font-weight: var(--od-weight-semibold); }
.od-modal__peek-bar span { margin-left: auto; color: var(--od-text-tertiary); }
.od-modal__peek-rows { margin: 0; padding: 0; list-style: none; }
.od-modal__peek-rows li { display: flex; align-items: center; gap: var(--od-space-3); padding: var(--od-space-3) 0; }
.od-modal__peek-rows li + li { border-top: 1px solid var(--od-border-subtle); }
.od-modal__peek-rows .od-appicon { --s: 32px; }
.od-modal__peek-name { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: var(--od-size-caption, .75rem); color: var(--od-text-secondary); }
.od-modal__peek-name b { color: var(--od-text); font-size: var(--od-size-footnote, .8125rem); font-weight: var(--od-weight-semibold); }
.od-modal__peek-name span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.od-modal__peek-date { flex: none; color: var(--od-text-tertiary); font-size: var(--od-size-caption, .75rem); font-variant-numeric: tabular-nums; }
@media (max-width: 600px) {
  .od-modal__peek-date { display: none; }
  .od-modal--peek .od-modal__title { font-size: var(--od-size-headline); }
  .od-modal--peek .od-modal__actions { flex-direction: column-reverse; }
  .od-modal--peek .od-modal__actions .od-btn { width: 100%; height: var(--od-control-height-lg); }
}

.od-sheet {
  position: fixed; z-index: var(--od-z-modal);
  left: 0; right: 0; bottom: 0;
  max-height: 86vh;
  padding: var(--od-space-3) var(--od-space-5) var(--od-space-8);
  border-radius: var(--od-radius-2xl) var(--od-radius-2xl) 0 0;
  background: var(--od-bg);
  box-shadow: var(--od-shadow-4);
  touch-action: none;
  will-change: transform;
}
/* 잡을 수 있다는 표시 */
.od-sheet__grabber {
  width: 36px; height: 5px; margin: 0 auto var(--od-space-4);
  border-radius: var(--od-radius-full);
  background: var(--od-gray-300);
}

/* ==========================================================================
   Toast — 알리고 스스로 사라진다
   ========================================================================== */
.od-toast-region {
  position: fixed; z-index: var(--od-z-toast);
  left: 50%; bottom: var(--od-space-6);
  display: flex; flex-direction: column; align-items: center; gap: var(--od-space-2);
  transform: translateX(-50%);
  pointer-events: none;
}
.od-toast {
  display: flex; align-items: center; gap: var(--od-space-3);
  max-width: min(420px, calc(100vw - 32px));
  padding: var(--od-space-3) var(--od-space-5);
  border-radius: var(--od-radius-full);
  background: rgba(29, 29, 31, .92);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  color: var(--od-text-on-accent);
  font-size: var(--od-size-subhead);
  box-shadow: var(--od-shadow-3);
  pointer-events: auto;
}
.od-toast__icon { flex: none; }

/* ==========================================================================
   Tooltip — 이름을 보충할 뿐, 없으면 못 쓰는 정보는 담지 않는다
   ========================================================================== */
.od-tooltip {
  position: absolute; z-index: var(--od-z-tooltip);
  padding: var(--od-space-1) var(--od-space-2);
  border-radius: var(--od-radius-sm);
  background: var(--od-gray-900);
  color: var(--od-text-on-accent);
  font-size: var(--od-size-caption);
  line-height: 1.4;
  white-space: nowrap;
  box-shadow: var(--od-shadow-2);
  pointer-events: none;
}

/* ==========================================================================
   Menu — 누른 것에서 자라난다
   ========================================================================== */
.od-menu {
  position: absolute; z-index: var(--od-z-menu);
  min-width: 180px;
  padding: var(--od-space-1);
  border: 1px solid var(--od-border);
  border-radius: var(--od-radius-lg);
  background: rgba(255, 255, 255, .86);
  backdrop-filter: saturate(180%) blur(24px);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  box-shadow: var(--od-shadow-3);
}
.od-menu__item {
  display: flex; align-items: center; gap: var(--od-space-3);
  width: 100%; padding: var(--od-space-2) var(--od-space-3);
  border: 0; border-radius: var(--od-radius-sm);
  background: none; color: var(--od-text);
  font-size: var(--od-size-subhead); text-align: left;
  cursor: pointer;
}
.od-menu__item:hover { background: var(--od-bg-hover); }
.od-menu__item--danger { color: var(--od-text-danger); }
.od-menu__item--danger:hover { background: var(--od-danger-subtle); }
.od-menu__sep { height: 1px; margin: var(--od-space-1) 0; background: var(--od-border-subtle); }

/* ==========================================================================
   Progress · Spinner · Skeleton
   ========================================================================== */
.od-progress { height: 6px; border-radius: var(--od-radius-full); background: var(--od-bg-sunken); overflow: hidden; }
.od-progress__bar {
  height: 100%; border-radius: inherit;
  background: var(--od-accent);
  transition: width var(--od-duration-slow) var(--od-ease-out);
}

.od-spinner {
  display: inline-block; width: 18px; height: 18px;
  border: 2px solid var(--od-gray-200);
  border-top-color: var(--od-accent);
  border-radius: 50%;
  animation: od-spin .7s linear infinite;
}
.od-spinner--lg { width: 28px; height: 28px; border-width: 3px; }

.od-skeleton {
  border-radius: var(--od-radius-sm);
  background: var(--od-gray-100);
  position: relative; overflow: hidden;
}
.od-skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .7), transparent);
  transform: translateX(-100%);
  animation: od-shimmer 1.4s ease-in-out infinite;
}
@keyframes od-shimmer { to { transform: translateX(100%); } }
.od-skeleton--text { height: 1em; }
@media (prefers-reduced-motion: reduce) { .od-skeleton::after { animation: none; } }

/* ==========================================================================
   Empty state — 비어 있는 이유와 다음 할 일을 같이 준다
   ========================================================================== */
.od-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--od-space-3);
  padding: var(--od-space-16) var(--od-space-6);
}
.od-empty__icon { color: var(--od-gray-300); }
.od-empty__title { font-size: var(--od-size-headline); font-weight: var(--od-weight-semibold); }
.od-empty__desc  { max-width: 34ch; color: var(--od-text-secondary); font-size: var(--od-size-subhead); }
.od-empty__action { margin-top: var(--od-space-2); }

/* ==========================================================================
   Breadcrumb · Divider · Kbd · Avatar
   ========================================================================== */
.od-breadcrumb { display: flex; align-items: center; gap: var(--od-space-2); font-size: var(--od-size-footnote); color: var(--od-text-secondary); }
.od-breadcrumb a { color: inherit; text-decoration: none; }
.od-breadcrumb a:hover { color: var(--od-text-accent); }
.od-breadcrumb__sep { color: var(--od-gray-300); }

.od-divider { height: 1px; border: 0; margin: var(--od-space-6) 0; background: var(--od-border-subtle); }

.od-kbd {
  display: inline-block; min-width: 20px; padding: 2px 6px;
  border: 1px solid var(--od-border); border-bottom-width: 2px;
  border-radius: var(--od-radius-xs);
  background: var(--od-bg);
  font-family: var(--od-font-mono); font-size: var(--od-size-caption);
  text-align: center; color: var(--od-text-secondary);
}

.od-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--od-gray-100);
  color: var(--od-text-secondary);
  font-size: var(--od-size-footnote); font-weight: var(--od-weight-medium);
  overflow: hidden; flex: none;
}
.od-avatar--lg { width: 44px; height: 44px; font-size: var(--od-size-subhead); }
.od-avatar--solid { background: var(--od-gray-900); color: var(--od-text-on-accent); }

/* ==========================================================================
   Layout 도우미
   ========================================================================== */
.od-container { width: 100%; max-width: var(--od-container-lg); margin: 0 auto; padding: 0 var(--od-space-5); }
.od-container--sm { max-width: var(--od-container-sm); }
.od-container--md { max-width: var(--od-container-md); }
.od-container--xl { max-width: var(--od-container-xl); }

.od-stack { display: flex; flex-direction: column; gap: var(--od-space-4); }
.od-stack--sm { gap: var(--od-space-2); }
.od-stack--lg { gap: var(--od-space-8); }
.od-cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--od-space-3); }
.od-grid { display: grid; gap: var(--od-space-4); grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }

/* 페이지 머리 */
.od-page-header { padding: var(--od-space-10) 0 var(--od-space-8); }
.od-page-header__title { font-size: var(--od-size-title-1); line-height: var(--od-leading-title-1); letter-spacing: var(--od-tracking-title-1); font-weight: var(--od-weight-semibold); margin: 0; }
.od-page-header__desc { margin: var(--od-space-3) 0 0; max-width: 60ch; color: var(--od-text-secondary); }

/* ==========================================================================
   문서 페이지 — oodot.top · account.oodot.top 과 앞으로 생길 oodot 페이지가 같이 쓰는 틀.
   상단바·본문·바닥이 한 틀(--od-container-page)의 같은 끝선에 선다.
   바탕은 흰색(--od-bg-page). 회색 바탕 위 흰 카드는 앱 화면(계정 관리·관리자)의 문법이다.
   ========================================================================== */
.od-frame { width: 100%; max-width: var(--od-container-page); margin: 0 auto; padding-left: var(--od-gutter); padding-right: var(--od-gutter); box-sizing: border-box; }

/* 첫 문장 — 페이지에 하나. 그림이 없는 첫 화면은 이 글자가 채운다 */
.od-hero {
  margin: 0; max-width: 17em;
  font-size: var(--od-size-hero); line-height: var(--od-leading-hero);
  letter-spacing: var(--od-tracking-hero); font-weight: var(--od-weight-bold);
  text-wrap: balance; word-break: keep-all;
}
.od-lede {
  margin: 0; max-width: 34em;
  font-size: clamp(var(--od-size-headline), 1rem + .4vw, var(--od-size-title-3));
  line-height: 1.7; color: var(--od-text-secondary); word-break: keep-all;
}

/* ── 상단바 ── 로고 옆에 이름. 기호만으로는 이름이 읽히지 않는다 */
.od-site-header {
  position: sticky; top: 0; z-index: var(--od-z-sticky);
  background: var(--od-bg);
  border-bottom: 1px solid var(--od-border-subtle);
}
.od-site-header__in { display: flex; align-items: center; height: 64px; }
.od-site-header__brand {
  display: inline-flex; align-items: center; gap: 10px; flex: none;
  height: 44px; padding-right: var(--od-space-2);
  color: var(--od-text); text-decoration: none;
  font-size: 1.1875rem; font-weight: var(--od-weight-bold); letter-spacing: -0.03em;
}
.od-site-header__brand img { display: block; width: 22px; height: 22px; }
.od-site-header__nav { display: flex; flex: 1; min-width: 0; margin-left: var(--od-space-8); overflow-x: auto; scrollbar-width: none; }
.od-site-header__nav::-webkit-scrollbar { display: none; }
.od-site-header__end { display: flex; align-items: center; flex: none; gap: var(--od-space-1); margin-left: auto; }
.od-site-header__link {
  display: inline-flex; align-items: center; flex: none;
  height: 64px; padding: 0 var(--od-space-3);
  color: var(--od-text-secondary); text-decoration: none;
  font-size: 0.9375rem; font-weight: var(--od-weight-medium);
  transition: color var(--od-duration-fast) var(--od-ease-inout);
}
.od-site-header__link:first-child { padding-left: 0; }
.od-site-header__end .od-site-header__link { padding: 0 var(--od-space-2); }
.od-site-header__link:hover { color: var(--od-text); }
/* 지금 있는 페이지 — 검정 글자 + 밑줄. 파랑은 누르는 것에만 쓴다 */
.od-site-header__link[aria-current="page"] { color: var(--od-text); box-shadow: inset 0 -2px 0 var(--od-gray-900); }
.od-site-header__link:focus-visible, .od-site-header__brand:focus-visible { outline-offset: -4px; }
/* 좁은 화면 — 이름·계정이 첫 줄, 메뉴는 둘째 줄에서 옆으로 민다. 한 줄에 다 넣으면 계정 링크가 메뉴에 묻힌다 */
@media (max-width: 600px) {
  .od-site-header__in { flex-wrap: wrap; height: auto; }
  .od-site-header__brand { height: 52px; }
  .od-site-header__end .od-site-header__link { height: 52px; padding-right: 0; }
  .od-site-header__nav { order: 3; flex-basis: 100%; margin: 0 calc(var(--od-gutter) * -1); padding: 0 var(--od-gutter); }
  .od-site-header__nav .od-site-header__link { height: 44px; }
}

/* ── 바닥 ── 어디로 갈 수 있고 누구에게 물을 수 있는지. 운영되는 곳이라는 신호가 여기서 나온다 */
.od-site-footer { border-top: 1px solid var(--od-border-subtle); background: var(--od-bg-page); font-size: var(--od-size-footnote); }
.od-site-footer__top {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--od-space-3) var(--od-space-8);
  padding-top: var(--od-space-6); padding-bottom: 0;
}
.od-site-footer .od-site-footer__brand { display: inline-flex; align-items: center; gap: 6px; align-self: center; color: var(--od-text); text-decoration: none; font-size: var(--od-size-subhead); font-weight: var(--od-weight-bold); letter-spacing: -0.02em; }
.od-site-footer .od-site-footer__brand:hover { text-decoration: none; }
.od-site-footer__brand img { width: 16px; height: 16px; }
/* 링크가 적어서 칸으로 나누지 않고 한 줄로 둔다. 서비스 분류와 그 밖은 옅은 세로선으로 가른다 */
.od-site-footer__links { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--od-space-2) var(--od-space-5); }
.od-site-footer__sep { align-self: center; width: 1px; height: 12px; background: var(--od-border); }
/* 약관 링크와 저작권 — 링크와 같은 줄 오른쪽 끝 */
.od-site-footer__legal { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--od-space-2) var(--od-space-5); margin-left: auto; color: var(--od-text-tertiary); font-size: var(--od-size-caption); }
.od-site-footer__legal a.is-privacy { color: var(--od-text); font-weight: var(--od-weight-semibold); }
.od-site-footer__top:last-child { padding-bottom: var(--od-space-6); }
.od-site-footer a { color: var(--od-text-secondary); text-decoration: none; }
.od-site-footer a:hover { color: var(--od-text); text-decoration: underline; text-underline-offset: 3px; }
.od-site-footer__bottom {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--od-space-2) var(--od-space-5);
  position: relative;
  padding-top: var(--od-space-1); padding-bottom: var(--od-space-6);
  color: var(--od-text-tertiary); font-size: var(--od-size-caption);
}
.od-site-footer__bottom a { color: var(--od-text-secondary); }
/* 개인정보 처리방침은 굵게 — 국내 서비스 바닥의 관례이고, 찾는 사람이 가장 많다 */
.od-site-footer__bottom a.is-privacy { color: var(--od-text); font-weight: var(--od-weight-semibold); }
/* 개인정보 처리방침·이용약관은 오른쪽 끝에, 저작권은 그 뒤 맨 끝에 붙인다 */
.od-site-footer__bottom .is-privacy { margin-left: auto; }
.od-site-footer__copy { order: 3; }
/* 한 줄 바닥 — 로그인처럼 할 일 하나에 집중하는 화면 */
.od-site-footer--compact { border-top: 0; }
.od-site-footer--compact .od-site-footer__bottom { padding-top: var(--od-space-6); }
@media (max-width: 600px) {
  .od-site-footer__top { flex-direction: column; align-items: flex-start; gap: var(--od-space-3); }
  .od-site-footer__legal { margin-left: 0; }
  .od-site-footer .od-site-footer__brand { align-self: flex-start; }
}

/* ── 서비스 아이콘 ── 서비스마다 그림은 달라도 칸 크기·모서리·테두리는 같다.
   아이콘이 없는 서비스는 검은 칸에 이름 첫 글자 — 회색 칸은 '아직 안 만든 것'처럼 보여 쓰지 않는다 */
.od-appicon {
  --s: 48px;
  position: relative; display: inline-flex; flex: none; align-items: center; justify-content: center;
  width: var(--s); height: var(--s); border-radius: var(--od-icon-radius); overflow: hidden;
  background: var(--od-bg);
}
.od-appicon img { display: block; width: 100%; height: 100%; object-fit: cover; }
.od-appicon::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: var(--od-icon-edge); pointer-events: none; }
.od-appicon--mono {
  background: var(--od-gray-900); color: var(--od-text-on-accent);
  font-size: calc(var(--s) * .44); font-weight: var(--od-weight-semibold); line-height: 1; letter-spacing: -0.02em;
}
.od-appicon--mono::after { box-shadow: none; }

/* ── 밖으로 나가는 링크 ── 다른 주소로 떠난다는 표시는 이 화살표 하나로 통일한다 */
.od-go { display: inline-flex; align-items: center; gap: 4px; color: var(--od-text-accent); font-size: var(--od-size-subhead); font-weight: var(--od-weight-medium); white-space: nowrap; }
.od-go svg { flex: none; width: 14px; height: 14px; }
.od-go--none { color: var(--od-text-tertiary); font-weight: var(--od-weight-regular); }
