  /* 팔레트는 5색으로 압축했다: 잉크 / 종이 / 카드 / 버건디 / 클레이.
     예전엔 랜덤 추천 버튼만 초록(--accent2 #3F6B63)이라 팔레트가 분산됐고, 다크 모드에서
     accent가 연분홍(#E08A90)으로 바뀌면서 라이트와 다른 브랜드처럼 보였다. 이제 다크도
     같은 버건디 색상환을 유지해서 두 모드가 한 브랜드로 읽히게 한다.
     (--accent2는 이름만 유지하고 값은 클레이로 바꿔, 기존 규칙을 다 고치지 않아도
     팔레트가 통일되게 했다.) */
  :root {
    --paper: #F2EDE4;
    --paper-soft: #E7DFD1;
    --paper-raised: #FBF8F2;
    --ink: #23201D;
    --ink-soft: #5C534A;
    /* 흰 배경 대비 4.8:1 — 예전 #94897C는 2.9:1로 AA 미달이었다. */
    --ink-faint: #6F6558;
    --accent: #7C2B36;
    --accent-soft: #A84A52;
    /* 흰 글자를 얹는 버튼(랜덤 추천)에 쓰이므로 4.9:1이 나오는 깊이로 잡았다. */
    --accent2: #A85C46;
    --accent2-soft: #C07253;
    --line: rgba(35, 32, 29, 0.13);
    --shadow: rgba(35, 32, 29, 0.10);
    --logo-front: #7C2B36;
    --logo-back: #C4837F;
    /* 채운 버튼은 라이트/다크 모두 "진한 버건디(또는 클레이) 바탕 + 밝은 글자"로 통일한다.
       텍스트 색인 --accent를 그대로 버튼 배경에 쓰면, 다크에서는 그 값이 밝은 로즈라
       버튼만 쨍하게 떠서 라이트와 인상이 달라졌다. */
    --btn-fill: #7C2B36;
    --btn-fill-hover: #963642;
    --btn-fill-ink: #FBF8F2;
    --btn-alt: #A85C46;
    --btn-alt-hover: #BE6D54;
    /* 등급 아이콘 5티어 색. 브랜드 계열 안에서 입문(밝은 클레이) → 전설(가장 깊은 버건디)로
       점점 진해지게 잡아, 모양뿐 아니라 색으로도 등급이 올라간 게 읽히게 한다. */
    --lv-bookmark: #A85C46;
    --lv-books: #A84A52;
    --lv-ghost: #7A4A52;
    --lv-owl: #8C3A38;
    --lv-crown: #6E2733;
  }

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --paper: #17140F;
      --paper-soft: #241F19;
      --paper-raised: #211D17;
      --ink: #EDE7DC;
      --ink-soft: #B5AA9B;
      --ink-faint: #8D8274;
      --accent: #D9848B;
      --accent-soft: #E8A0A6;
      --accent2: #D89B84;
      --accent2-soft: #E8B49F;
      --line: rgba(237, 231, 220, 0.14);
      --shadow: rgba(0, 0, 0, 0.45);
      --logo-front: #D9848B;
      --logo-back: #8C4E56;
      --btn-fill: #8C3A44;
      --btn-fill-hover: #A34551;
      --btn-fill-ink: #FBEDED;
      --btn-alt: #A15942;
      --btn-alt-hover: #B96A4F;
      /* 어두운 바탕에서는 같은 순서로 흐린 톤 → 밝은 톤으로 올라간다(라이트의 반대 방향이
         아니라, 낮은 등급이 눈에 덜 띄고 높은 등급이 도드라진다는 관계를 유지한 것). */
      --lv-bookmark: #C08268;
      --lv-books: #CE7F86;
      --lv-ghost: #C295A0;
      --lv-owl: #E0918C;
      --lv-crown: #F0B9BE;
    }
  }

  :root[data-theme="dark"] {
    --paper: #17140F;
    --paper-soft: #241F19;
    --paper-raised: #211D17;
    --ink: #EDE7DC;
    --ink-soft: #B5AA9B;
    --ink-faint: #8D8274;
    --accent: #D9848B;
    --accent-soft: #E8A0A6;
    --accent2: #D89B84;
    --accent2-soft: #E8B49F;
    --line: rgba(237, 231, 220, 0.14);
    --shadow: rgba(0, 0, 0, 0.45);
    --logo-front: #D9848B;
    --logo-back: #8C4E56;
    --btn-fill: #8C3A44;
    --btn-fill-hover: #A34551;
    --btn-fill-ink: #FBEDED;
    --btn-alt: #A15942;
    --btn-alt-hover: #B96A4F;
    /* 어두운 바탕에서는 같은 순서로 흐린 톤 → 밝은 톤으로 올라간다(라이트의 반대 방향이
       아니라, 낮은 등급이 눈에 덜 띄고 높은 등급이 도드라진다는 관계를 유지한 것). */
    --lv-bookmark: #C08268;
    --lv-books: #CE7F86;
    --lv-ghost: #C295A0;
    --lv-owl: #E0918C;
    --lv-crown: #F0B9BE;
  }

  * { box-sizing: border-box; }

  /* 타이포 역할 분리: 제목/책 제목/로고는 명조(Gowun Batang), 본문·UI는 고딕.
     본문 고딕을 Gowun Dodum에서 Noto Sans KR로 바꾼 이유 — Gowun Dodum은 굵기가 400
     하나뿐이라 라벨/섹션 제목을 굵게 만들 수단이 없었고(위계가 평평해진 근본 원인),
     획이 얇아 작은 글씨에서 흐릿했다. 이제 400/500/700을 쓴다. */
  body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: "Noto Sans KR", "Malgun Gothic", sans-serif;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
  }

  /* 타이포 스케일 6단계. 예전엔 제목(2rem)과 본문(0.95rem) 사이가 비어 있어서
     "책 소개", "한줄평·별점" 같은 섹션 제목이 본문과 같은 무게로 묻혔다. */
  :root {
    --t-display: 2rem;     /* 인트로 헤드라인 */
    --t-title: 1.4rem;     /* 책 제목 */
    --t-section: 1.06rem;  /* 섹션 제목 (새로 생긴 단계) */
    --t-body: 0.95rem;     /* 본문 */
    --t-meta: 0.82rem;     /* 보조 정보 */
    --t-micro: 0.72rem;    /* 라벨·배지 */
  }

  .wrap {
    max-width: 920px;
    margin: 0 auto;
    padding: 2.5rem 1.5rem 5rem;
  }

  /* icons */

  /* 선형 아이콘 공통 규격. 크기를 em으로 묶어두면 놓이는 자리의 font-size를 그대로
     따라가므로 자리마다 픽셀을 다시 잡을 필요가 없다. vertical-align은 글자 가운데에
     맞춘다 — 기본값(baseline)이면 아이콘이 글자보다 아래로 처져 보인다.
     path의 stroke는 currentColor라 색은 놓이는 자리의 color가 정한다. */
  .icon {
    width: 1.05em;
    height: 1.05em;
    flex: none;
    vertical-align: -0.19em;
    /* fill을 심볼의 fill="none" 속성이 아니라 여기서 준다. 프리젠테이션 속성은 상속값을
       이기기 때문에, 심볼 path에 fill="none"이 박혀 있으면 .star-icon.is-filled에서
       fill: currentColor를 줘도 안쪽이 칠해지지 않는다(별이 전부 빈 별로 나왔다). */
    fill: none;
  }

  /* 스프라이트 정의용 svg는 자리를 차지하면 안 된다. width=0으로도 대개 사라지지만,
     일부 브라우저가 인라인 요소로 한 줄을 잡는 경우가 있어 확실히 접어둔다. */
  .icon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
  }

  /* 별점: 채운 별과 빈 별이 같은 path를 쓰고 안쪽을 칠하는지만 다르다. 별 모양이 서로
     어긋나지 않아 5칸이 정확히 같은 폭으로 나란히 선다. */
  .star-icon {
    width: 1em;
    height: 1em;
    color: var(--ink-faint);
  }

  .star-icon.is-filled {
    color: var(--accent);
    fill: currentColor;
  }

  .star-row {
    display: inline-flex;
    align-items: center;
    gap: 0.06em;
  }

  /* 별점 선택 위젯 안에서는 아이콘이 자기 색을 고집하지 않고 버튼 색을 따라간다 —
     .star-picker button이 이미 기본/선택(.filled)/호버 색을 다 갖고 있어서, 그 규칙을
     그대로 쓰는 편이 색을 두 군데서 관리하지 않아도 된다. */
  .star-picker button .star-icon {
    color: inherit;
  }

  /* 책 표지(책등) 위에 얹히는 별점은 어두운 바탕이라 버건디로는 안 읽힌다 — 크림색으로
     바꾸고, 빈 별은 같은 크림을 옅게 깔아 채운 별과 구분되게 한다. */
  .book-card .b-stars .star-icon {
    color: #FBF8F2;
  }

  .book-card .b-stars .star-icon:not(.is-filled) {
    color: rgba(251, 248, 242, 0.5);
  }

  /* 등급 아이콘. 티어마다 색을 달리 줘서 모양과 색 두 가지로 등급이 구분된다. */
  .lv-icon {
    margin-right: 0.24em;
  }

  .lv-icon--bookmark { color: var(--lv-bookmark); }
  .lv-icon--books { color: var(--lv-books); }
  .lv-icon--ghost { color: var(--lv-ghost); }
  .lv-icon--owl { color: var(--lv-owl); }
  .lv-icon--crown { color: var(--lv-crown); }

  /* 좋아요 하트: 누르지 않은 상태는 윤곽선만, 누르면 안쪽까지 칠해진다. */
  .like-icon {
    width: 0.95em;
    height: 0.95em;
  }

  .liked .like-icon {
    fill: currentColor;
  }

  /* header */

  .sticky-header {
    position: sticky;
    top: 0;
    z-index: 30;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
  }

  /* 헤더 자동 숨김은 헤더가 두 줄로 접히는 폭(=아래 900px 줄바꿈 배치와 같은 구간)에서
     의미가 있다. 그 구간에서는 헤더가 두 줄이라 세로를 많이 먹어서, 스크롤할 때 접히지
     않으면 정작 읽으려는 리뷰가 가려진다. 한 줄로 들어가는 900px 위에서는 헤더가 얕아
     굳이 숨길 이유가 없다. js/main.js의 스크롤 리스너는 폭과 무관하게 항상 동작하지만,
     transform 효과 자체를 이 미디어쿼리 안에 가둬서 PC에서는 클래스가 붙어도 아무 일도
     일어나지 않는다. */
  @media (max-width: 900px) {
    .sticky-header {
      transition: transform 0.45s ease;
    }

    .sticky-header.header-hidden {
      transform: translateY(-100%);
    }
  }

  .site-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.55rem 0;
  }

  /* 헤더는 브랜드+닉네임+알림+버튼 4개가 한 줄에 들어가려면 900px 가까이 필요하다.
     640px에서 곧바로 데스크톱 한 줄 배치로 되돌리면 641~880px 구간에서 헤더가 가로로
     넘쳐 "+ 책장에 추가하기" 버튼이 화면 밖으로 잘렸다(페이지도 가로 스크롤됨).
     그래서 이 줄바꿈 배치를 900px까지 유지한다. */
  @media (max-width: 900px) {
    .site-header {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: flex-start;
      row-gap: 0;
      column-gap: 0.5rem;
      padding: 0.5rem 0;
    }

    /* header-actions 자체는 박스를 만들지 않고, 그 자식들(auth-box/notif-box/
       버튼들)이 site-header의 flex 아이템으로 직접 들어와서 order로 재배치될
       수 있게 한다. 데스크톱 폭에서는 이 규칙이 적용되지 않으므로
       header-actions가 그대로 하나의 그룹으로 유지된다(레이아웃 그대로).
       선택자에 .site-header를 붙여 우선순위를 올린 이유: 스타일시트 뒤쪽에
       있는 기존 ".header-actions { display:flex; ... }" 규칙과 우선순위가
       동점이면 나중에 나온 규칙이 이겨버려서 여기서 지정한 display:contents가
       무시되는 문제가 있었다. */
    .site-header .header-actions {
      display: contents;
    }

    .brand { order: 1; }
    .header-actions .auth-box { order: 2; }
    .header-actions .notif-box { order: 3; }

    .header-actions .auth-box:empty {
      display: none;
    }

    /* 닉네임(auth-box)·알림 벨을 오른쪽으로 붙여서 브랜드와의 사이에만 큰
       여백이 생기게 한다. 닉네임이 저장되어 있으면 auth-box에 그 여백을
       주고, 아직 없으면(auth-box가 비어 사라진 상태) 바로 다음 알림 벨이
       대신 그 여백을 떠안는다. */
    .header-actions .auth-box:not(:empty) {
      margin-left: auto;
    }

    .header-actions .auth-box:empty + .notif-box {
      margin-left: auto;
    }

    /* 폭 100%짜리 빈 아이템으로 강제 줄바꿈을 만든다 — 이 아이템은 항상
       자기 혼자 한 줄을 차지하므로, order가 더 큰(20번대) 버튼들은 전부
       다음 줄로 밀려난다. */
    .site-header::after {
      content: "";
      order: 10;
      flex-basis: 100%;
      width: 0;
      height: 0.5rem;
    }

    .header-actions #feedbackBtn { order: 20; }
    .header-actions #homeBtn { order: 21; }
    .header-actions #randomBtn { order: 22; }
    .header-actions #newReviewBtn { order: 23; }

    .header-actions .btn-accent-outline,
    .header-actions .btn-random,
    .header-actions .btn-primary {
      /* min-width를 명시적으로 0으로 풀지 않는다 — 기본값(auto)이어야 라벨 안의
         white-space: nowrap 텍스트(예: new-review-btn-hint) 폭만큼은 자동으로
         확보되고, 그보다 좁아져야 할 때는 다음 줄로 줄바꿈되어 버튼 밖으로
         글자가 삐져나오는 대신 자연스럽게 두 줄로 배치된다. */
      flex: 1 1 auto;
      padding: 0.5rem 0.6rem;
      font-size: 0.82rem;
      text-align: center;
    }

    /* 터치 타겟 확대: 애플/구글 권장 최소 크기(44px)에 맞춘다. */
    .notif-bell {
      width: 44px;
      height: 44px;
    }
  }

  .brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: clamp(1.2rem, 2.4vw, 1.45rem);
    /* 등급 배지가 붙어 닉네임(.user-name)이 길어지면서 데스크톱 폭이 좁아졌을 때
       "책갈피" 로고 텍스트 쪽이 밀려서 두 줄로 쪼개지는 문제가 있었다. 로고는 항상
       줄어들지 않게 고정하고, 대신 닉네임 쪽(.user-chip)이 줄어들며 자체적으로
       줄바꿈되게 한다. */
    flex-shrink: 0;
  }

  .logo-symbol {
    display: flex;
    align-items: flex-start;
    gap: 0.11em;
    flex: none;
  }

  .logo-bar {
    width: 0.36em;
    border-radius: 0.13em 0.13em 0 0;
  }

  .logo-bar--front {
    height: 1.5em;
    background: var(--logo-front);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 84%, 0 100%);
  }

  .logo-bar--back {
    height: 1.15em;
    background: var(--logo-back);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 80%, 0 100%);
  }

  .brand-name {
    margin: 0;
    font-family: "Gowun Batang", "Malgun Gothic", serif;
    font-weight: 700;
    font-size: 1em;
    letter-spacing: 0.02em;
  }

  /* PC(넓은 화면)에서만 로고 텍스트/심볼을 키운다. 모바일은 위 기본값을 그대로 유지. */
  @media (min-width: 641px) {
    .brand {
      gap: 0.75rem;
      font-size: clamp(1.9rem, 2.6vw, 2.4rem);
    }

    .brand-name {
      font-weight: 800;
    }
  }

  /* 책갈피 리본: 사용자가 남긴 것(한줄평·답글·방금 등록된 기록)의 왼쪽에 공통으로 붙는
     표식. 끝을 V자로 파서 실제 책갈피 모양이 되게 했다 — 둥근 카드+그림자로 구분하는
     흔한 방식 대신, 이 서비스의 이름값을 하는 시각 장치를 하나 정해 반복한다. */
  .ribbon-mark {
    position: relative;
    padding-left: 0.95rem;
  }

  .ribbon-mark::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.2em;
    width: 4px;
    height: 1.45em;
    background: var(--ribbon, var(--accent));
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 76%, 0 100%);
  }

  .header-intro {
    margin: 1.6rem 0 2.4rem;
  }

  /* 검색·분류로 걸러 인트로를 접으면 인트로가 갖고 있던 아래 여백(2.4rem)까지 같이
     사라져서 결과가 도구 막대에 딱 붙는다. 접혔을 때만 목록이 그만큼을 대신 준다.
     .shelf는 목록 화면 안에만 있으므로 상세·등록 화면에는 영향이 없다. */
  .header-intro[hidden] + main .shelf {
    margin-top: 1.5rem;
  }

  .intro-headline {
    margin: 0;
    color: var(--ink);
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: var(--t-display);
    line-height: 1.25;
    letter-spacing: -0.01em;
  }

  .intro-subtext {
    margin: 0.5rem 0 0;
    color: var(--ink-soft);
    font-size: var(--t-body);
    font-weight: 400;
  }

  /* 참여를 권하는 줄. 위 설명(intro-subtext)과 구분되게 브랜드 색을 쓰되, 크기는 한 단계
     줄이고 무게만 살짝 올려서 "권유"로 읽히되 헤드라인을 가리지 않게 한다. */
  .intro-invite {
    margin: 0.35rem 0 0;
    color: var(--accent);
    font-size: var(--t-meta);
    font-weight: 500;
  }

  .new-review-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    line-height: 1.15;
  }

  .new-review-btn-hint {
    font-size: 0.68rem;
    font-weight: 400;
    opacity: 0.85;
    white-space: nowrap;
  }

  .btn-primary {
    flex: none;
    background: var(--btn-fill);
    color: var(--btn-fill-ink);
    border: none;
    border-radius: 3px;
    padding: 0.65rem 1.1rem;
    font-family: inherit;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.15s ease;
  }

  .btn-primary:hover { background: var(--btn-fill-hover); }
  .btn-primary:active { transform: translateY(1px); }
  .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

  .btn-random {
    flex: none;
    background: var(--btn-alt);
    color: var(--btn-fill-ink);
    border: none;
    border-radius: 3px;
    padding: 0.65rem 1.1rem;
    font-family: inherit;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.15s ease;
  }

  .btn-random:hover { background: var(--btn-alt-hover); }
  .btn-random:active { transform: translateY(1px); }

  .btn-primary:focus-visible,
  button:focus-visible,
  input:focus-visible,
  textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* auth */

  .header-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    /* 900px 위에서도 안전장치를 둔다 — 등급 배지가 붙은 긴 닉네임처럼 내용이
       길어지면 한 줄에 다 못 들어갈 수 있다. flex:none으로 고정해두면 그때
       버튼이 화면 밖으로 잘리므로, 줄바꿈을 허용해 아래로 흐르게 한다. */
    flex: 0 1 auto;
    flex-wrap: wrap;
    justify-content: flex-end;
    row-gap: 0.5rem;
    min-width: 0;
  }

  .auth-box { display: flex; align-items: center; min-width: 0; }

  .user-chip {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* flex 아이템의 기본 min-width는 auto라서 내용(닉네임 전체)만큼의 폭을 절대
       내주지 않는다 — 그러면 옆의 .brand가 대신 밀려서 두 줄로 쪼개진다. 0으로
       풀어서 폭이 부족할 때 이 칩이 먼저 줄어들고, 그 안의 .user-name(줄바꿈 허용)이
       필요하면 스스로 2줄로 넘어가게 한다. */
    min-width: 0;
  }

  /* 헤더의 내 닉네임과 한줄평의 남의 닉네임은 이제 눌러서 그 사람 기록으로 가는 버튼이다.
     버튼 기본 모양(테두리·배경·여백)을 지워서 겉보기는 예전 그대로 두고, 누를 수 있다는
     신호는 커서와 호버 밑줄로만 준다 — 목록에 알약 버튼이 줄줄이 생기면 정작 읽어야 할
     한줄평보다 닉네임이 더 눈에 띈다. */
  .user-chip.is-linked,
  .c-author.is-linked,
  .c-reply-author.is-linked {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-align: left;
  }

  .user-chip.is-linked:hover .user-name,
  .c-author.is-linked:hover,
  .c-reply-author.is-linked:hover {
    text-decoration: underline;
    text-underline-offset: 0.2em;
  }

  .user-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    flex: none;
    background: var(--paper-soft);
  }

  .user-name {
    /* 한줄평 목록의 닉네임(#commentList .c-author)과 같은 크기·색상으로 맞춘다. 등급까지
       붙어 길어질 수 있어 고정 max-width+ellipsis로 자르지 않는다 — 대신 nowrap을 안
       걸어서, 닉네임이 아주 길 때(최대 10자)는 이 span 안에서 자연스럽게 줄바꿈되게
       한다(화면 밖으로 흘러넘치는 대신). 보통 길이의 닉네임은 한 줄에 그대로 들어간다. */
    font-size: 1rem;
    font-weight: 700;
    color: var(--accent);
    min-width: 0;
  }

  .btn-logout {
    background: none;
    border: none;
    color: var(--ink-faint);
    font-family: inherit;
    font-size: 0.8rem;
    cursor: pointer;
    text-decoration: underline;
    padding: 0;
  }

  .btn-logout:hover { color: var(--accent); }

  /* notifications */

  .notif-box { position: relative; display: flex; align-items: center; }

  .notif-bell {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: none;
    border: none;
    color: var(--ink-soft);
    cursor: pointer;
    border-radius: 50%;
    transition: background 0.15s ease, color 0.15s ease;
  }

  .notif-bell svg { width: 17px; height: 17px; }

  .notif-bell:hover { background: var(--paper-soft); color: var(--ink); }

  .notif-dot {
    position: absolute;
    top: 3px;
    right: 4px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--paper-raised);
  }

  .notif-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 260px;
    max-width: calc(100vw - 2rem);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    padding: 0.5rem 0;
    z-index: 30;
  }

  .notif-title {
    margin: 0;
    padding: 0.15rem 0.9rem 0.5rem;
    font-family: "Gowun Batang", serif;
    font-size: 0.92rem;
    font-weight: 700;
    color: var(--ink);
    border-bottom: 1px solid var(--line);
  }

  .notif-empty {
    margin: 0;
    padding: 0.8rem 0.9rem 0.3rem;
    font-size: 0.85rem;
    color: var(--ink-faint);
  }

  .notif-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 280px;
    overflow-y: auto;
  }

  .notif-item {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--ink);
    padding: 0.55rem 0.9rem;
    cursor: pointer;
  }

  .notif-item:hover { background: var(--paper-soft); }

  .signin-prompt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.6rem;
    background: var(--paper-soft);
    border: 1px dashed var(--line);
    border-radius: 4px;
    padding: 0.8rem 1rem;
    font-size: 0.88rem;
    color: var(--ink-soft);
  }

  /* library grid */

  .library-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.5rem 0 0.65rem;
    border-top: 1px solid var(--line);
  }

  @media (max-width: 640px) {
    .library-toolbar {
      padding: 0.45rem 0 0.55rem;
      gap: 0.5rem;
    }

    .sort-tabs {
      flex: 1 1 100%;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      padding-bottom: 0.1rem;
    }

    .sort-tab {
      flex: none;
      padding: 0.35rem 0.7rem;
      font-size: 0.78rem;
    }

    .filter-row {
      flex: 1 1 100%;
      gap: 0.5rem;
    }

    .select-wrap {
      flex: 1 1 0;
      min-width: 0;
    }

    .category-filter {
      width: 100%;
    }

    .search-box {
      flex: 1 1 0;
      min-width: 0;
    }

    .search-box input[type="search"] {
      width: 100%;
    }
  }

  .library-meta {
    color: var(--ink-faint);
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    margin: 0;
    font-variant-numeric: tabular-nums;
  }

  .sort-tabs {
    display: flex;
    gap: 0.4rem;
  }

  .sort-tab {
    background: none;
    border: 1px solid var(--line);
    color: var(--ink-soft);
    border-radius: 999px;
    padding: 0.4rem 0.85rem;
    font-family: inherit;
    font-size: 0.82rem;
    cursor: pointer;
  }

  .sort-tab:hover { border-color: var(--ink-soft); color: var(--ink); }

  .sort-tab.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--paper-raised);
  }

  .search-box input[type="search"] {
    width: 220px;
    max-width: 100%;
    font-family: inherit;
    font-size: 0.9rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.45rem 0.9rem;
  }

  .filter-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
  }

  /* 네이티브 <select>의 브라우저 기본 화살표 아이콘이 옆의 정렬 탭/검색창과
     톤이 안 맞아 튀어 보여서, appearance:none으로 지우고 테마 색을 따라가는
     CSS 화살표(::after)를 대신 그린다(currentColor를 못 쓰는 배경 이미지 방식
     대신 실제 색상 변수를 쓰는 요소라 라이트/다크 전환에도 안전하다). */
  .select-wrap {
    position: relative;
    display: inline-flex;
  }

  .select-wrap::after {
    content: "";
    position: absolute;
    right: 0.8rem;
    top: 50%;
    width: 6px;
    height: 6px;
    border-right: 1.5px solid var(--ink-faint);
    border-bottom: 1.5px solid var(--ink-faint);
    transform: translateY(-70%) rotate(45deg);
    pointer-events: none;
  }

  .category-filter {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    font-family: inherit;
    font-size: 0.82rem;
    color: var(--ink-soft);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.4rem 1.7rem 0.4rem 0.85rem;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
  }

  .category-filter:hover { border-color: var(--ink-soft); color: var(--ink); }

  /* 모바일 iOS Safari에서 16px 미만 입력창/셀렉트는 포커스 시 자동 확대되므로 16px
     유지한다. 분류 필터가 이 처리를 안 받아서 검색창보다 글자가 작고 위축돼
     보였던 것도 같이 고친다. */
  @media (max-width: 640px) {
    .search-box input[type="search"],
    .category-filter {
      font-size: 16px;
    }

    .category-filter {
      padding: 0.45rem 2rem 0.45rem 0.9rem;
    }
  }

  .shelf {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1.1rem;
  }

  /* 카드 표지 위 오버레이 텍스트 전용 고정 밝은 톤. 사이트 전체 라이트/다크 테마
     전환과 무관하게 항상 밝게 유지한다(표지 사진 위에 겹치므로 테마색을 따라가면
     다크 모드에서 어두운 텍스트가 어두운 표지와 섞여 안 읽힘). */
  :root {
    --card-info-ink: #F3EEE4;
  }

  /* 책은 물건이니 그림자로 살짝 떠 있는 게 맞지만, 예전의 0 3px 10px는 모든 카드가
     똑같이 붕 뜬 느낌이라 위계가 없었다. 그림자를 눌러 종이 두께 정도로만 남기고
     모서리도 2px로 각지게 해서 '책등이 꽂힌 서가'에 가깝게 만든다. */
  .book-card {
    position: relative;
    display: block;
    aspect-ratio: 2 / 3;
    text-align: left;
    color: var(--card-info-ink);
    text-decoration: none;
    border: none;
    border-radius: 2px;
    padding: 0;
    overflow: hidden;
    cursor: pointer;
    font-family: inherit;
    box-shadow: 0 1px 3px var(--shadow);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
  }

  .book-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 5px 14px var(--shadow);
  }

  /* PC 기본: 표지가 카드 전체를 채우고 그 위에 .b-overlay가 절대위치로 겹친다(기존 동작
     그대로). 모바일에서만 아래 미디어쿼리가 이걸 "표지 위/아래로 분리된" 레이아웃으로 바꾼다. */
  .b-cover {
    position: absolute;
    inset: 0;
    background: var(--cover, var(--accent));
  }

  /* 책등 색(js/render.js COVERS)은 라이트 종이 배경 기준으로 고른 중간 명도라, 다크에서는
     카드만 밝게 떠 보인다. 색을 테마별로 따로 두는 대신 여기서 한 단계 눌러준다. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .b-cover,
    :root:not([data-theme="light"]) .detail-cover {
      filter: brightness(0.82) saturate(0.92);
    }
  }

  :root[data-theme="dark"] .b-cover,
  :root[data-theme="dark"] .detail-cover {
    filter: brightness(0.82) saturate(0.92);
  }

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

  .b-new-badge {
    position: absolute;
    top: 0.55rem;
    right: 0.55rem;
    z-index: 3;
    /* 예전 #E0483C(주황빨강)는 팔레트 밖 색이라 서가에서 혼자 튀었다. 브랜드 버건디로
       맞추고, 알약 대신 모서리를 각지게 해 다른 카드 요소와 결을 맞춘다. */
    background: #7C2B36;
    color: #FBF8F2;
    font-size: var(--t-micro);
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 0.16rem 0.42rem;
    border-radius: 2px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  }

  /* 표지 없는 책(.book-card--typo): 예전엔 의미 없는 단색 블록이었다. 제목을 명조로 크게
     조판한 "책등"으로 바꿔서, 빈칸을 메우는 색이 아니라 정보가 되는 시각물이 되게 한다.
     아래 오버레이의 제목은 중복이라 감추고 별점만 남긴다. */
  .book-card--typo .b-cover::before {
    content: attr(data-title);
    position: absolute;
    /* 아래 3.4rem은 별점 줄 자리 — 제목은 그 위 영역의 세로 중앙에 놓아야
       위쪽에 붙어 가운데가 텅 비어 보이지 않는다. */
    inset: 0 0 3.4rem 0;
    display: flex;
    align-items: center;
    padding: 1rem 0.85rem;
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: 1.2rem;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--card-info-ink);
    overflow: hidden;
  }

  /* 책등 위쪽에 얇은 가로선 — 실제 책등의 상단 띠를 흉내낸 디테일 */
  .book-card--typo .b-cover::after {
    content: "";
    position: absolute;
    left: 0.85rem;
    right: 0.85rem;
    bottom: 3.1rem;
    height: 1px;
    background: rgba(243, 238, 228, 0.28);
  }

  .book-card--typo .b-title {
    display: none;
  }

  .b-overlay {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    padding: 0.85rem 0.8rem 0.75rem;
    background: linear-gradient(to top, rgba(10, 8, 6, 0.85) 0%, rgba(10, 8, 6, 0.55) 48%, rgba(10, 8, 6, 0) 100%);
  }

  /* 책등 카드는 배경이 단색이라 그라데이션이 얼룩처럼 보인다 — 오버레이는 투명하게 두고
     별점만 얹는다. */
  .book-card--typo .b-overlay {
    background: none;
  }

  .book-card .b-title {
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1.35;
    text-wrap: balance;
    margin: 0 0 0.4rem;
  }

  /* 별점(★★★★☆)+평균 점수+참여자 수가 카드 폭 안에서 줄바꿈 없이 한 줄로
     들어가야 한다. 참여자 수 두 자리(예: 99)까지는 iPhone 기준(375px 폭
     이상) 카드에서 한 줄에 맞지만, 그보다 좁은 안드로이드 폰(~360px)에서는
     빠듯해 끝이 말줄임(...)으로 잘릴 수 있다 — white-space: nowrap +
     text-overflow: ellipsis로 넘쳐도 레이아웃은 깨지지 않게만 막아둔 것. */
  .book-card .b-stars {
    display: flex;
    align-items: center;
    gap: 0.2em;
    font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* 참여자가 없거나 한 명뿐일 때 별점 자리에 들어가는 안내 문구(js/render.js). 별점보다
     한 단계 작고 흐리게 둬서, 정보가 아니라 거드는 말로 읽히게 한다. */
  .book-card .b-stars--invite {
    font-size: 0.82rem;
    opacity: 0.88;
  }

  /* 카드 위에 마우스를 올렸을 때(또는 키보드 포커스·터치로 누르는 동안) 아래에서 밀려
     올라오는 행동 유도 문구. 평소에는 max-height 0으로 완전히 접어둬서 카드 정보가
     가려지지 않고, 나타날 때만 오버레이가 그만큼 자라난다.
     색은 테마 토큰을 쓰지 않고 고정값이다 — 이 자리는 표지 사진이든 단색 책등이든 항상
     어두운 배경 위라서, 라이트 모드에서도 밝은 글자여야 읽힌다(.b-stars의 크림색과 같은
     이유). 버건디 계열의 밝은 로즈로 골라 브랜드 색과 이어지게 했다. */
  .book-card .b-cta {
    display: block;
    margin-top: 0;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transform: translateY(3px);
    color: #F2C2BC;
    font-size: 0.78rem;
    font-weight: 500;
    white-space: nowrap;
    transition: max-height 0.18s ease, opacity 0.18s ease, transform 0.18s ease, margin-top 0.18s ease;
  }

  .book-card:active .b-cta,
  .book-card:focus-visible .b-cta {
    max-height: 1.6em;
    margin-top: 0.3rem;
    opacity: 1;
    transform: none;
  }

  /* 마우스가 있는 기기에서만 hover로 연다. 터치 기기에서 hover 규칙을 그대로 두면 탭한
     카드에 hover 상태가 남아 문구가 계속 떠 있는 경우가 있다(iOS Safari). */
  @media (hover: hover) {
    .book-card:hover .b-cta {
      max-height: 1.6em;
      margin-top: 0.3rem;
      opacity: 1;
      transform: none;
    }
  }

  /* 터치 기기에는 hover가 없다. :active도 기대기 어렵다 — 탭하면 곧바로 상세로 넘어가서
     보일 틈이 없고, iOS에서는 조건에 따라 아예 적용되지 않기도 한다. 그래서 참여가 필요한
     카드(리뷰 0~1개, js/render.js가 book-card--invite를 붙인다)에서만 처음부터 보여준다.
     모든 카드에 띄우면 좁은 화면에서 줄만 늘어나고, 이미 리뷰가 쌓인 책은 눌러볼 이유가
     문구 없이도 충분하다. */
  @media (hover: none) {
    .book-card--invite .b-cta {
      max-height: 1.6em;
      margin-top: 0.3rem;
      opacity: 1;
      transform: none;
    }
  }

  /* 움직임을 줄이도록 설정한 사용자에게는 밀려 올라오는 연출 없이 그냥 나타나게 한다. */
  @media (prefers-reduced-motion: reduce) {
    .book-card .b-cta { transition: none; transform: none; }
  }

  .empty-note {
    color: var(--ink-faint);
    font-size: 0.9rem;
    padding: 1.5rem 0;
  }

  /* 검색 결과가 없을 때 뜨는 안내 묶음(안내 문구 + 권유 + 등록 버튼). .shelf가 카드용
     그리드라서 그 안에서는 자식마다 한 칸씩 차지하고 사이에 카드 간격이 들어간다 —
     한 칸에 묶어 전체 폭을 쓰게 하고, 내부 간격은 여백으로 직접 잡는다. */
  .shelf .empty-cta {
    grid-column: 1 / -1;
  }

  /* 묶음 안에서는 세 줄이 한 문단처럼 붙어 읽혀야 한다. */
  .empty-cta .empty-note {
    padding: 1.25rem 0 0.3rem;
  }

  .empty-note-sub {
    margin: 0 0 0.9rem;
    color: var(--ink-soft);
    font-size: 0.9rem;
  }

  .empty-note-cta {
    background: var(--btn-fill);
    color: var(--btn-fill-ink);
    border: none;
    border-radius: 2px;
    padding: 0.55rem 1rem;
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s ease;
  }

  .empty-note-cta:hover { background: var(--btn-fill-hover); }
  .empty-note-cta:active { transform: translateY(1px); }

  /* 모바일 카드도 PC와 동일하게 표지 위에 그라데이션 오버레이로 제목/저자/별점을
     겹쳐 보여준다(표지 아래 별도 정보 박스로 분리했던 이전 방식은 카드가 세로로
     길어져 한 화면에 보이는 책 수가 줄어드는 문제가 있어 되돌림). */
  @media (max-width: 640px) {
    .wrap {
      padding: 1.75rem 1rem 3.5rem;
    }

    .shelf {
      grid-template-columns: repeat(2, 1fr);
      gap: 0.75rem;
    }
  }

  /* form */

  /* 종이 한 장 위에 쓰는 느낌 — 둥근 모서리(6px)를 2px로 줄이고 그림자 없이
     테두리만으로 면을 잡는다. 사이트 전체가 같은 규칙(각진 종이)을 따른다. */
  .panel {
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 2px;
    padding: 1.75rem;
  }

  /* ── 닉네임 프로필 화면 ────────────────────────────────────────────── */

  .profile-head {
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--line);
  }

  .profile-name {
    margin: 0;
    color: var(--ink);
    font-family: "Gowun Batang", serif;
    font-size: 1.5rem;
    font-weight: 700;
  }

  .profile-sub {
    margin: 0.35rem 0 0;
    color: var(--accent);
    font-size: 0.9rem;
  }

  /* 요약 숫자. dt(항목명)/dd(값)를 세로로 짝지어 한 칸씩 놓고, 좁아지면 칸 수가 줄어든다. */
  .profile-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
    gap: 0.9rem 1rem;
    margin: 1.25rem 0 0;
    padding: 0;
  }

  .profile-stats dt {
    color: var(--ink-faint);
    font-size: 0.78rem;
  }

  .profile-stats dd {
    margin: 0.15rem 0 0;
    color: var(--ink);
    font-family: "Gowun Batang", serif;
    font-size: 1.25rem;
    font-weight: 700;
  }

  .profile-section {
    margin-top: 2.25rem;
  }

  .profile-section h3 {
    margin: 0 0 0.9rem;
    color: var(--ink);
    font-size: 1rem;
  }

  .profile-section h3 .count {
    color: var(--ink-faint);
    font-size: 0.85rem;
    font-weight: 400;
  }

  /* 홈 서가와 같은 칸 크기를 쓴다. 더 좁게 잡아봤더니 카드 하단의 "4.6 (5)" 같은 별점
     줄이 잘려서, 폭을 아끼는 것보다 카드가 멀쩡히 보이는 쪽이 낫다. */
  .profile-shelf {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  }

  .profile-reviews {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .profile-reviews li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--line);
  }

  .profile-reviews li:last-child { border-bottom: none; }

  .pr-book {
    flex: none;
    max-width: 100%;
    color: var(--accent);
    font-weight: 700;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .pr-book:hover { text-decoration: underline; }

  .pr-rating {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 0.08em;
    color: var(--accent);
    font-size: 1rem;
  }

  .pr-text {
    flex: 1 1 200px;
    min-width: 0;
    color: var(--ink);
    font-size: 0.92rem;
    word-break: break-word;
  }

  .pr-date {
    flex: none;
    margin-left: auto;
    color: var(--ink-faint);
    font-size: 0.8rem;
  }

  /* 좁은 화면에서는 책 제목이 한 줄을 다 쓰고 별점·한줄평이 아래로 내려오게 둔다.
     제목이 잘려서 어느 책인지 모르게 되는 게 제일 곤란하다. */
  @media (max-width: 640px) {
    .pr-book { flex-basis: 100%; }
    .pr-text { flex-basis: 100%; }
    .pr-date { margin-left: 0; }
  }

  /* 모바일에서는 .wrap 자체 여백(1rem)이 이미 있으므로 패널 내부 여백까지
     PC와 동일하게 두면 좁은 화면에서 입력 영역이 과하게 줄어든다. */
  @media (max-width: 640px) {
    .panel {
      padding: 1.25rem 1.1rem;
    }
  }

  /* "책 소개"/"한줄평·별점"과 같은 섹션 제목 규칙(위쪽 짧은 버건디 선)을 패널 제목에도
     그대로 적용해, 화면이 달라도 제목의 생김새가 일관되게 한다. */
  .panel h2 {
    position: relative;
    font-family: "Gowun Batang", serif;
    margin: 0 0 1.5rem;
    padding-top: 0.8rem;
    font-size: var(--t-title);
  }

  .panel h2::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 1.6rem;
    height: 2px;
    background: var(--accent);
  }

  .field {
    margin-bottom: 1.15rem;
  }

  /* 라벨은 흐린 회색 대신 잉크색 + 500 굵기로. 폼에서 라벨은 읽어야 하는 정보지
     장식이 아니다. */
  .field label {
    display: block;
    font-size: var(--t-meta);
    font-weight: 500;
    color: var(--ink);
    letter-spacing: 0.01em;
    margin-bottom: 0.35rem;
  }

  /* 입력창은 카드(--paper-raised, 거의 흰 종이) 위에 놓이므로 배경을 한 단계 눌러
     "여기에 쓴다"는 자리로 읽히게 하고, 테두리를 또렷하게 해 비활성처럼 보이지 않게 한다. */
  .field input[type="text"],
  .field input[type="email"],
  .field textarea {
    width: 100%;
    font-family: inherit;
    font-size: 1rem;
    color: var(--ink);
    background: var(--paper);
    border: 1px solid var(--line);
    border-bottom-color: var(--ink-faint);
    border-radius: 2px;
    padding: 0.6rem 0.7rem;
    resize: vertical;
    transition: border-color 0.15s ease, background 0.15s ease;
  }

  .field input[type="text"]:focus,
  .field input[type="email"]:focus,
  .field textarea:focus {
    border-color: var(--accent);
    background: var(--paper-raised);
  }

  .field textarea { min-height: 110px; line-height: 1.6; }

  .book-search-row {
    display: flex;
    gap: 0.6rem;
  }

  .book-search-row input {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 1rem;
    color: var(--ink);
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.6rem 0.7rem;
  }

  .book-results {
    list-style: none;
    margin: 0.8rem 0 0;
    padding: 0;
    max-height: 380px;
    overflow-y: auto;
    border: 1px solid var(--line);
    border-radius: 4px;
  }

  .book-results li + li { border-top: 1px solid var(--line); }

  .book-result-empty {
    padding: 0.9rem;
    color: var(--ink-faint);
    font-size: 0.88rem;
  }

  .book-result-item {
    display: flex;
    align-items: center;
    gap: 1rem;
    width: 100%;
    background: none;
    border: none;
    padding: 0.85rem 0.9rem;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    color: var(--ink);
  }

  .book-result-item:hover { background: var(--paper-soft); }

  .book-result-item img,
  .book-result-noimg {
    width: 68px;
    aspect-ratio: 2 / 3;
    flex: none;
    border-radius: 4px;
    object-fit: cover;
    background: var(--paper-soft);
    box-shadow: 0 2px 6px var(--shadow);
  }

  .book-result-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.68rem;
    color: var(--ink-faint);
    text-align: center;
    line-height: 1.3;
  }

  .book-result-title {
    font-size: 1.02rem;
    line-height: 1.4;
  }

  .book-result-author {
    font-size: 0.85rem;
    color: var(--ink-faint);
    margin-top: 0.2rem;
  }

  .selected-book {
    display: flex;
    align-items: center;
    gap: 1rem;
    background: var(--paper-soft);
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: 0.9rem 1rem;
  }

  .selected-book-cover {
    width: 76px;
    aspect-ratio: 2 / 3;
    flex: none;
    border-radius: 4px;
    overflow: hidden;
    background: var(--cover, var(--accent));
    box-shadow: 0 2px 8px var(--shadow);
  }

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

  .selected-book-info { flex: 1; min-width: 0; }

  .selected-book-title {
    font-family: "Gowun Batang", serif;
    font-size: 1.05rem;
  }

  .selected-book-author {
    font-size: 0.85rem;
    color: var(--ink-faint);
    margin-top: 0.2rem;
  }

  .star-picker {
    display: flex;
    gap: 0.25rem;
  }

  .star-picker button {
    background: none;
    border: none;
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    color: var(--ink-faint);
    padding: 0.1rem;
  }

  .star-picker button.filled { color: var(--accent); }
  .star-picker button:hover { color: var(--accent-soft); }

  /* 감정 태그 고르기(선택 항목). 홈의 정렬 탭(.sort-tab)과 같은 알약 모양을 써서 "눌러
     고르는 것"이라는 게 설명 없이 읽히게 했다. 하나만 고를 수 있고, 고른 것만 채운다. */
  .mood-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
  }

  .mood-chip {
    background: none;
    border: 1px solid var(--line);
    color: var(--ink-soft);
    border-radius: 999px;
    padding: 0.4rem 0.8rem;
    font-family: inherit;
    font-size: 0.82rem;
    line-height: 1.2;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
  }

  .mood-chip:hover { border-color: var(--ink-soft); color: var(--ink); }

  .mood-chip.is-on {
    background: var(--btn-fill);
    border-color: var(--btn-fill);
    color: var(--btn-fill-ink);
  }

  .mood-chip.is-on:hover {
    background: var(--btn-fill-hover);
    border-color: var(--btn-fill-hover);
    color: var(--btn-fill-ink);
  }

  /* 목록에 붙는 태그 배지. 채운 알약은 고르는 버튼(.mood-chip.is-on) 몫이라, 읽기용인
     여기서는 옅은 테두리만 둘러 본문보다 앞서 보이지 않게 한다. */
  .c-mood {
    display: inline-block;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    color: var(--accent);
    font-size: 0.78rem;
    line-height: 1.5;
    white-space: nowrap;
  }

  .form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.5rem;
  }

  .btn-ghost {
    background: none;
    border: 1px solid var(--line);
    color: var(--ink-soft);
    border-radius: 3px;
    padding: 0.6rem 1rem;
    font-family: inherit;
    cursor: pointer;
  }

  .btn-ghost:hover { border-color: var(--ink-soft); color: var(--ink); }

  .btn-accent-outline {
    background: none;
    border: 1px solid var(--accent);
    color: var(--accent);
    border-radius: 3px;
    padding: 0.6rem 1rem;
    font-family: inherit;
    font-weight: 600;
    cursor: pointer;
  }

  .btn-accent-outline:hover { background: var(--accent); color: var(--paper-raised); }

  /* detail */

  .detail-top {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-bottom: 1.4rem;
    padding-bottom: 1.4rem;
    border-bottom: 1px solid var(--line);
    perspective: 1200px;
  }

  .detail-cover {
    position: relative;
    width: clamp(160px, 26vw, 240px);
    aspect-ratio: 2 / 3;
    flex: none;
    border-radius: 6px;
    overflow: hidden;
    background: var(--cover, var(--accent));
    box-shadow: 0 4px 14px var(--shadow);
    transform-origin: left center;
    backface-visibility: hidden;
  }

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

  /* 표지 없는 책의 상세 페이지: 목록 카드와 같은 책등 조판. 예전엔 화면의 1/3을
     정보량 0인 단색 블록이 차지했다. */
  .detail-cover--typo::before {
    content: attr(data-title);
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    padding: 1.5rem 1.2rem;
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: 1.45rem;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--card-info-ink);
    overflow: hidden;
  }

  /* (목록 카드에 있는 책등 가로선은 여기선 생략한다 — .detail-cover.cover-in::after가
     진입 애니메이션용으로 ::after를 이미 쓰고 있어 충돌한다.) */

  /* 상세 페이지 진입 시, 랜덤 뽑기 결과 카드와 같은 방식(perspective + rotateY)으로
     표지가 옆에서 펼쳐지듯 나타난다. 재렌더링(8초 폴링)마다 재생되지 않도록 이 클래스는
     진입 시 한 번만 JS에서 붙였다 뗀다 (render.js의 state.detailCoverAnimatePending 참고). */
  .detail-cover.cover-in {
    animation: randomPageIn 0.35s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    will-change: transform, opacity;
  }

  .detail-cover.cover-in::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.5), transparent 60%);
    animation: randomPageFold 0.35s ease-out both;
    pointer-events: none;
  }

  /* 표지 펼침 애니메이션이 끝난 직후 곧바로 제목/저자/별점이 떠오르듯 나타난다. */
  .detail-card header.info-in {
    animation: detailInfoIn 0.22s ease-out both;
    animation-delay: 0.32s;
  }

  @keyframes detailInfoIn {
    0% { opacity: 0; transform: translateY(6px); }
    100% { opacity: 1; transform: translateY(0); }
  }

  .detail-card header {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    flex: 1;
    min-width: 200px;
  }

  @media (max-width: 640px) {
    .detail-top {
      flex-direction: column;
      align-items: center;
      text-align: center;
      gap: 1rem;
    }

    .detail-cover {
      width: clamp(180px, 55vw, 260px);
    }

    .detail-card header {
      align-items: center;
      min-width: 0;
      width: 100%;
    }

    .rating-line {
      justify-content: center;
    }
  }

  /* 책 제목은 섹션 제목이 아니라 그 페이지의 주인공이므로, .panel h2에 붙는 짧은 선은
     빼고 크기로만 위계를 준다. */
  .detail-card h2 {
    font-family: "Gowun Batang", serif;
    font-size: clamp(1.7rem, 2.6vw, 2.05rem);
    margin: 0;
    padding-top: 0;
    text-wrap: balance;
  }

  .detail-card h2::before {
    content: none;
  }

  .detail-author { color: var(--ink-soft); margin: 0; font-size: 1.15rem; }

  /* 별점이 이제 svg라 letter-spacing이 안 먹는다 — 간격은 flex gap으로 준다. */
  .stars { color: var(--accent); font-size: 1.5rem; display: flex; align-items: center; gap: 0.12em; }
  .stars .empty { color: var(--ink-faint); }

  .rating-line {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
  }

  .rating-meta {
    color: var(--ink-faint);
    font-size: 1rem;
    font-variant-numeric: tabular-nums;
  }

  .meta-date {
    color: var(--ink-faint);
    font-size: 0.8rem;
    margin: 0.2rem 0 0;
    font-variant-numeric: tabular-nums;
  }

  .meta-owner {
    color: var(--ink-faint);
    font-size: 0.8rem;
    margin: 0.25rem 0 0;
  }

  /* 한줄평 목록의 닉네임(#commentList .c-author)과 정확히 같은 크기·스타일로 맞춘다. */
  .meta-owner-name {
    color: var(--accent);
    font-weight: 700;
    font-size: 1rem;
  }

  .btn-ghost-danger {
    background: none;
    border: 1px solid var(--line);
    color: var(--ink-faint);
    border-radius: 3px;
    padding: 0.35rem 0.7rem;
    font-family: inherit;
    font-size: 0.75rem;
    cursor: pointer;
    margin-top: 2rem;
  }

  .btn-ghost-danger:hover { border-color: var(--accent); color: var(--accent); }

  .book-contents {
    margin-top: 1.2rem;
    padding-top: 1.2rem;
    border-top: 1px solid var(--line);
  }

  .book-contents h3 {
    position: relative;
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: var(--t-section);
    margin: 0 0 0.6rem;
    padding-top: 0.7rem;
  }

  .book-contents h3::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 1.6rem;
    height: 2px;
    background: var(--accent);
  }

  .book-contents-text {
    margin: 0;
    color: var(--ink-soft);
    font-size: 0.92rem;
    line-height: 1.55;
    white-space: pre-line;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .book-contents-text.expanded {
    -webkit-line-clamp: unset;
    overflow: visible;
  }

  .book-contents-toggle {
    background: none;
    border: none;
    color: var(--accent);
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    padding: 0.4rem 0 0;
    cursor: pointer;
  }

  .book-contents-toggle:hover { text-decoration: underline; }

  @media (max-width: 640px) {
    .book-contents-text {
      -webkit-line-clamp: 2;
      font-size: 0.88rem;
    }
  }

  .comments {
    margin-top: 2.25rem;
  }

  /* 섹션 제목(--t-section)은 본문과 확실히 구분되도록 명조 + 위쪽에 짧은 버건디 선을
     둔다. 예전엔 본문과 거의 같은 무게라 어디서 섹션이 시작되는지 안 보였다. */
  .comments h3 {
    position: relative;
    font-family: "Gowun Batang", serif;
    font-weight: 700;
    font-size: var(--t-section);
    margin: 0 0 1.1rem;
    padding-top: 0.7rem;
  }

  .comments h3::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 1.6rem;
    height: 2px;
    background: var(--accent);
  }

  .comments h3 .count {
    color: var(--ink-faint);
    font-weight: 400;
    font-size: 0.85rem;
  }

  #commentList {
    list-style: none;
    margin: 0 0 1.1rem;
    padding: 0;
  }

  /* 한줄평 한 줄 = 책 사이에 끼운 책갈피 한 장. 왼쪽 리본이 이 서비스의 공통 표식이고
     (.ribbon-mark, .latest-highlight-card와 같은 V자 모양), 한줄평은 버건디 / 답글은
     클레이로 색만 달리해 원글과 답글이 색으로도 구분되게 했다.
     absolute라 flex 아이템이 되지 않으므로 기존 order 기반 배치는 그대로다. */
  #commentList li {
    position: relative;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    padding: 0.85rem 0 0.85rem 0.95rem;
    border-bottom: 1px solid var(--line);
    font-size: var(--t-body);
  }

  #commentList > li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.95rem;
    width: 4px;
    height: 1.4em;
    background: var(--accent);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 76%, 0 100%);
  }

  /* 데스크톱 기본값: 닉네임+별점(왼쪽) · 리뷰 텍스트(가운데, 넓게) · 좋아요+답글+날짜(오른쪽)를
     한 줄에 배치한다. order로만 시각 순서를 잡아서, 모바일 미디어쿼리에서 순서만 다시
     바꾸면 되고 마크업(render.js)은 건드릴 필요가 없다. */
  #commentList .c-author {
    order: 1;
    flex: none;
    color: var(--accent);
    font-weight: 700;
    font-size: 1rem;
    margin-right: 0.15rem;
  }

  #commentList .c-rating {
    order: 2;
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 0.08em;
    color: var(--accent);
    font-size: 1.05rem;
    margin-right: 0.35rem;
  }

  /* order를 본문과 같은 3으로 둔다 — 같은 값이면 DOM 순서를 따르고, render.js가 배지를
     본문보다 먼저 넣으므로 결과적으로 본문 바로 앞에 놓인다. 기존 order 값(1·2·4·5)을
     하나도 건드리지 않으려고 이렇게 했다. */
  #commentList .c-mood {
    order: 3;
    flex: none;
    margin-right: 0.4rem;
  }

  #commentList .c-text {
    order: 3;
    flex: 1 1 220px;
    min-width: 0;
    word-break: break-word;
  }

  #commentList .c-like {
    order: 4;
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.3rem;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--ink-faint);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    transition: color 0.15s ease, border-color 0.15s ease;
  }

  #commentList .c-like:hover { border-color: var(--accent); color: var(--accent); }

  #commentList .c-like.liked {
    color: var(--accent);
    border-color: var(--accent);
  }

  #commentList .c-reply-btn {
    order: 5;
    flex: none;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--ink-faint);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
  }

  #commentList .c-reply-btn:hover { border-color: var(--accent); color: var(--accent); }

  #commentList .c-date {
    order: 6;
    flex: none;
    color: var(--ink-faint);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
  }

  #commentList .c-del {
    order: 7;
    flex: none;
    display: flex;
    align-items: center;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0.15rem 0.5rem;
    transition: color 0.15s ease, border-color 0.15s ease;
  }

  #commentList .c-del:hover { color: var(--accent); border-color: var(--accent); }

  /* 수정 버튼은 삭제 버튼과 같은 알약 모양으로 나란히 둔다. order를 .c-del과 같은 7로
     두면 DOM에서 먼저 붙는 이쪽이 왼쪽에 온다(js/render.js에서 수정 버튼을 삭제 버튼보다
     앞서 append한다). */
  #commentList .c-edit {
    order: 7;
    flex: none;
    display: flex;
    align-items: center;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0.15rem 0.5rem;
  }

  #commentList .c-edit:hover { color: var(--accent); border-color: var(--accent); }

  /* 수정 폼은 고치려는 댓글 바로 아래(답글 목록보다 위)에 와야 무엇을 고치는 중인지
     읽힌다. 그래서 .c-replies(9)·.c-reply-form(10)보다 앞 순서를 쓴다. */
  #commentList .c-edit-form {
    order: 8;
    flex: 1 1 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.4rem;
  }

  #commentList .c-edit-form input[type="text"] {
    flex: 1 1 12rem;
    min-width: 0;
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.35rem 0.6rem;
  }

  #commentList .c-edit-form button {
    border: none;
    border-radius: 3px;
    padding: 0.35rem 0.8rem;
    font-family: inherit;
    font-size: 0.8rem;
    cursor: pointer;
  }

  #commentList .c-edit-save {
    background: var(--btn-fill);
    color: var(--btn-fill-ink);
  }

  #commentList .c-edit-save:hover { background: var(--btn-fill-hover); }

  #commentList .c-edit-cancel {
    background: none;
    color: var(--ink-soft);
    border: 1px solid var(--line) !important;
  }

  #commentList .c-edit-cancel:hover { color: var(--ink); border-color: var(--ink-faint) !important; }

  /* 수정 중인 별점 위젯은 한줄평 줄 안에 들어가므로 작성 폼의 큰 별(1.6rem)보다 작고
     촘촘하게. .star-picker button이 크기를 직접 지정하므로 여기서도 button을 짚어야
     한다 — 컨테이너 font-size만 줄이면 안 먹는다. */
  #commentList .c-edit-stars {
    flex: none;
    gap: 0.1rem;
  }

  #commentList .c-edit-stars button {
    font-size: 1.05rem;
    padding: 0;
  }

  /* 답글 수정 버튼/폼. 답글 줄은 별점이 없어 본문만 고친다. */
  #commentList .c-reply-edit {
    flex: none;
    display: flex;
    align-items: center;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 0.8rem;
    line-height: 1;
    padding: 0.15rem 0.5rem;
  }

  #commentList .c-reply-edit:hover { color: var(--accent); border-color: var(--accent); }

  #commentList .c-reply-edit-form {
    order: 9;
    margin-top: 0.35rem;
  }

  #commentList .c-replies {
    order: 9;
    flex: 1 1 100%;
    list-style: none;
    margin: 0.3rem 0 0;
    padding: 0.5rem 0 0 1.1rem;
    border-top: 1px dashed var(--line);
  }

  #commentList .c-replies li {
    position: relative;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    /* 좋아요/날짜/삭제 버튼이 원댓글 줄과 세로로 맞도록 열 간격(column-gap)을
       원댓글(#commentList li)과 동일한 1rem으로 맞춘다. */
    gap: 0.35rem 1rem;
    padding: 0.4rem 0 0.4rem 0.85rem;
    font-size: var(--t-meta);
  }

  /* 답글 리본은 클레이색 — 원글(버건디)과 한눈에 구분된다. */
  #commentList .c-replies li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.55rem;
    width: 3px;
    height: 1.2em;
    background: var(--accent2);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 76%, 0 100%);
  }

  #commentList .c-reply-text {
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-word;
  }

  #commentList .c-reply-author {
    flex: none;
    color: var(--accent);
    font-weight: 700;
    font-size: 0.92rem;
    margin-right: 0.35rem;
  }

  /* 원댓글 줄의 좋아요 다음에는 "답글" 버튼이 있지만 답글 줄에는 없다. 좋아요 버튼끼리
     세로로 줄이 맞도록, 같은 위치에 보이지 않는 같은 너비의 자리표시자를 둔다. */
  #commentList .c-reply-btn-spacer {
    flex: none;
    visibility: hidden;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
  }

  #commentList .c-reply-like {
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.3rem;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--ink-faint);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    transition: color 0.15s ease, border-color 0.15s ease;
  }

  #commentList .c-reply-like:hover { border-color: var(--accent); color: var(--accent); }

  #commentList .c-reply-like.liked {
    color: var(--accent);
    border-color: var(--accent);
  }

  #commentList .c-reply-date {
    flex: none;
    color: var(--ink-faint);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
  }

  #commentList .c-reply-del {
    flex: none;
    display: flex;
    align-items: center;
    background: none;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 0.8rem;
    line-height: 1;
    padding: 0.15rem 0.5rem;
    transition: color 0.15s ease, border-color 0.15s ease;
  }

  #commentList .c-reply-del:hover { color: var(--accent); border-color: var(--accent); }

  #commentList .c-reply-form {
    order: 10;
    flex: 1 1 100%;
    display: flex;
    gap: 0.5rem;
    margin-top: 0.4rem;
    padding-left: 1.1rem;
  }

  #commentList .c-reply-form input[type="text"] {
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.35rem 0.6rem;
  }

  #commentList .c-reply-name {
    flex: 0 0 6rem;
  }

  #commentList .c-reply-text-input {
    flex: 1 1 auto;
    min-width: 0;
  }

  #commentList .c-reply-form button {
    background: var(--accent);
    color: var(--paper-raised);
    border: none;
    border-radius: 3px;
    padding: 0 0.8rem;
    font-family: inherit;
    font-size: 0.8rem;
    cursor: pointer;
  }

  #commentList .c-reply-form button:hover { background: var(--accent-soft); }

  /* 모바일: 1번째 줄 = 닉네임+별점(나란히), 2번째 줄 = 리뷰 텍스트, 좋아요+답글은
     그 뒤로 이어지고 자리가 없으면 다음 줄로 넘어간다. 날짜는 생략. */
  @media (max-width: 640px) {
    #commentList .c-author {
      order: 1;
      /* 한줄평의 등급 배지는 등급명 없이 "이모지 레벨 닉네임"뿐이라(js/levels.js
         formatNicknameShort) 닉네임이 최대 10자여도 별점과 한 줄에 충분히 들어간다.
         혹시 안 들어가면 flex-wrap에 맡겨 별점이 다음 줄로 자연스럽게 넘어간다. */
      white-space: nowrap;
      margin-right: 0.5rem;
    }

    #commentList .c-rating {
      order: 2;
    }

    #commentList .c-text {
      order: 3;
      flex-basis: 100%;
      min-width: 0;
    }

    #commentList .c-reply-btn-spacer {
      display: none;
    }

    /* 터치 타겟 확대: PC 기준 패딩(0.15rem)은 손가락으로 누르기엔 너무 좁아서
       모바일에서만 여유를 준다. */
    #commentList .c-like,
    #commentList .c-reply-btn,
    #commentList .c-reply-like {
      padding: 0.35rem 0.75rem;
    }

    #commentList .c-like {
      order: 4;
      margin-left: auto;
    }

    #commentList .c-reply-btn {
      order: 5;
    }

    #commentList .c-date {
      display: none;
    }

    #commentList .c-del {
      order: 6;
    }

    /* 데스크톱에선 날짜(order 6)가 사이에 있지만 모바일은 날짜를 감추므로, 수정 버튼도
       삭제와 같은 6으로 내려 좋아요·답글 바로 뒤에 붙게 한다. */
    #commentList .c-edit {
      order: 6;
    }

    #commentList .c-del,
    #commentList .c-edit,
    #commentList .c-reply-del,
    #commentList .c-reply-edit {
      padding: 0.3rem 0.4rem;
    }

    /* iOS Safari가 16px 미만 입력창에 포커스하면 화면을 확대해버린다 — 수정창도 동일 처리. */
    #commentList .c-edit-form input[type="text"] {
      font-size: 16px;
    }

    #commentList .c-reply-form {
      flex-direction: column;
      align-items: stretch;
    }

    #commentList .c-reply-name {
      flex: none;
      width: 100%;
    }

    /* 모바일 iOS Safari에서 16px 미만 입력창은 포커스 시 자동 확대되므로 16px 유지 */
    #commentList .c-reply-form input[type="text"] {
      font-size: 16px;
    }
  }

  /* 예전엔 한줄평 목록 바로 아래에 폼이 아무 구분 없이 이어져서, 남의 글과 내 입력창의
     경계가 없었다. 살짝 다른 바탕색 + 여백으로 "내가 쓰는 자리"를 하나의 구역으로 묶는다. */
  #commentForm {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1.4rem;
    padding: 1.1rem 1.1rem 1.2rem;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: 2px;
  }

  #commentForm .field {
    margin-bottom: 0;
  }

  #commentForm .field label {
    font-size: 0.78rem;
    margin-bottom: 0.15rem;
  }

  .comment-input-row {
    display: flex;
    gap: 0.6rem;
  }

  #commentInput {
    flex: 1;
    min-width: 0;
    font-family: inherit;
    font-size: 0.95rem;
    color: var(--ink);
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.55rem 0.7rem;
  }

  /* 모바일 iOS Safari에서 16px 미만 입력창은 포커스 시 자동 확대되므로 16px 유지 */
  @media (max-width: 640px) {
    #commentInput {
      font-size: 16px;
    }
  }

  .comment-input-row button {
    flex: none;
    white-space: nowrap;
    background: var(--accent);
    color: var(--paper-raised);
    border: none;
    border-radius: 3px;
    padding: 0 1rem;
    font-family: inherit;
    cursor: pointer;
  }

  .comment-input-row button:hover { background: var(--accent-soft); }

  .char-hint {
    font-size: 0.72rem;
    color: var(--ink-faint);
    margin: 0.35rem 0 0;
  }

  .latest-highlight {
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem;
    margin: 1.4rem 0 0;
  }

  /* 종이 조각 느낌: 둥근 모서리+그림자 대신 각진 카드 + 왼쪽 리본. 리본은 위 .ribbon-mark와
     같은 V자 모양을 카드 높이 전체에 세로로 세운 형태다. */
  .latest-highlight-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    flex: 1 1 260px;
    min-width: 0;
    background: var(--paper-raised);
    border: 1px solid var(--line);
    border-radius: 2px;
    padding: 0.8rem 0.95rem 0.85rem 1.35rem;
    text-align: left;
    color: inherit;
    text-decoration: none;
    font-family: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
  }

  .latest-highlight-card::before {
    content: "";
    position: absolute;
    left: 0.55rem;
    top: 0.8rem;
    bottom: 0.8rem;
    width: 4px;
    background: var(--accent);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 92%, 0 100%);
  }

  .latest-highlight-card:hover {
    border-color: var(--accent);
    background: var(--paper);
  }

  .latest-highlight-label {
    display: inline-flex;
    align-self: flex-start;
    color: var(--ink-faint);
    font-size: var(--t-micro);
    font-weight: 500;
    letter-spacing: 0.04em;
  }

  .latest-highlight-book {
    color: var(--ink);
    font-size: var(--t-meta);
  }

  .latest-highlight-book strong {
    font-weight: 700;
  }

  .latest-highlight-stars {
    color: var(--accent);
    font-size: 0.78rem;
  }

  .latest-highlight-text {
    margin: 0;
    color: var(--ink-soft);
    font-size: 0.85rem;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }

  /* random pick */

  .random-panel { text-align: center; }
  .random-panel h2 { text-align: left; }
  .random-panel .char-hint { text-align: left; margin-top: -0.6rem; }

  .random-stage {
    display: flex;
    justify-content: center;
    padding: 1.75rem 0 0.5rem;
    perspective: 1200px;
  }

  .random-card {
    position: relative;
    width: clamp(220px, 40vw, 320px);
    max-width: 80vw;
    border-radius: 8px;
    overflow: hidden;
    background: var(--paper-soft);
    box-shadow: 0 8px 22px var(--shadow);
    transform-origin: left center;
    backface-visibility: hidden;
  }

  .random-card-cover {
    position: relative;
    aspect-ratio: 2 / 3;
    background: var(--cover, var(--accent));
  }

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

  .random-card.spinning {
    animation: randomShake 0.12s linear infinite;
  }

  @keyframes randomShake {
    0% { transform: translateY(0) rotate(0deg); }
    50% { transform: translateY(-2px) rotate(-0.6deg); }
    100% { transform: translateY(0) rotate(0.6deg); }
  }

  /* 뽑기 결과가 확정되는 순간, 책장이 왼쪽 책등을 축으로 휙 펼쳐지는 느낌의 트랜지션.
     transform(rotateY)/opacity만 애니메이션해서 모바일에서도 레이아웃 재계산 없이
     가볍게 돌아간다. */
  .random-card.page-in {
    animation: randomPageIn 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    will-change: transform, opacity;
  }

  .random-card.page-in::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.5), transparent 60%);
    animation: randomPageFold 0.5s ease-out both;
    pointer-events: none;
  }

  @keyframes randomPageIn {
    0% { transform: rotateY(-100deg); opacity: 0; }
    55% { opacity: 1; }
    100% { transform: rotateY(0deg); opacity: 1; }
  }

  @keyframes randomPageFold {
    0% { opacity: 0.9; }
    100% { opacity: 0; }
  }

  .random-info { margin-top: 0.75rem; }

  /* 카드가 펼쳐지는 애니메이션을 뒤따라, 결과 정보도 살짝 뜸을 두고 떠오르듯 나타난다. */
  .random-info:not([hidden]) {
    animation: randomInfoIn 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: 0.12s;
  }

  @keyframes randomInfoIn {
    0% { opacity: 0; transform: translateY(8px); }
    100% { opacity: 1; transform: translateY(0); }
  }

  .random-info h3 {
    font-family: "Gowun Batang", serif;
    font-size: 1.35rem;
    margin: 0 0 0.3rem;
    text-wrap: balance;
  }

  .random-info-review {
    margin: 1rem auto 0;
    max-width: 480px;
    padding: 0.9rem 1.1rem;
    background: var(--paper-soft);
    border-left: 3px solid var(--accent);
    border-radius: 3px;
    font-size: 0.95rem;
    color: var(--ink);
    text-align: left;
  }

  .random-actions { justify-content: center; }

  /* 책 뽑기 버튼을 카드 위로 옮기면서, 설명 문구 바로 아래에 붙게 여백을 줄인다. */
  .random-draw-actions {
    margin-top: 1rem;
    margin-bottom: 0;
  }

  /* 연속 뽑기 이스터에그: 짧은 시간 안에 여러 번 뽑으면 잠깐 나타나는 토스트 문구. */
  .random-streak-msg {
    display: inline-block;
    margin: 0.6rem 0 0;
    padding: 0.45rem 0.95rem;
    background: var(--accent-soft);
    color: var(--paper-raised);
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 600;
    opacity: 0;
    transform: translateY(4px) scale(0.96);
    transition: opacity 0.25s ease, transform 0.25s ease;
  }

  .random-streak-msg.show {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  [hidden] { display: none !important; }

  /* 등록 마일스톤(50권 단위) 축하 연출 */

  .milestone-overlay {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0);
    pointer-events: none;
    transition: background 0.25s ease;
  }

  .milestone-overlay.show {
    background: rgba(0, 0, 0, 0.35);
    pointer-events: auto;
  }

  .milestone-modal {
    position: relative;
    background: var(--paper-raised);
    border-radius: 10px;
    box-shadow: 0 10px 30px var(--shadow);
    padding: 2.4rem 2.2rem 2rem;
    max-width: 320px;
    text-align: center;
    opacity: 0;
    transform: scale(0.9) translateY(8px);
    transition: opacity 0.25s ease, transform 0.25s ease;
  }

  .milestone-overlay.show .milestone-modal {
    opacity: 1;
    transform: scale(1) translateY(0);
  }

  .milestone-message {
    margin: 0;
    font-family: "Gowun Batang", serif;
    font-size: 1.15rem;
    color: var(--ink);
  }

  .milestone-confetti {
    position: absolute;
    inset: -6px -10px auto -10px;
    height: 90px;
    overflow: visible;
    pointer-events: none;
  }

  .milestone-confetti span {
    position: absolute;
    top: 0;
    width: 7px;
    height: 12px;
    border-radius: 1px;
    opacity: 0;
  }

  .milestone-overlay.show .milestone-confetti span {
    animation: confettiFall 1.1s ease-in forwards;
  }

  .milestone-confetti span:nth-child(1)  { left: 4%;  background: var(--accent); }
  .milestone-confetti span:nth-child(2)  { left: 13%; background: var(--accent2); animation-delay: .04s; }
  .milestone-confetti span:nth-child(3)  { left: 22%; background: var(--accent-soft); animation-delay: .1s; }
  .milestone-confetti span:nth-child(4)  { left: 32%; background: var(--accent2-soft); animation-delay: .02s; }
  .milestone-confetti span:nth-child(5)  { left: 42%; background: var(--accent); animation-delay: .12s; }
  .milestone-confetti span:nth-child(6)  { left: 52%; background: var(--accent2); animation-delay: .06s; }
  .milestone-confetti span:nth-child(7)  { left: 61%; background: var(--accent-soft); animation-delay: .08s; }
  .milestone-confetti span:nth-child(8)  { left: 70%; background: var(--accent2-soft); animation-delay: 0s; }
  .milestone-confetti span:nth-child(9)  { left: 79%; background: var(--accent); animation-delay: .1s; }
  .milestone-confetti span:nth-child(10) { left: 87%; background: var(--accent2); animation-delay: .05s; }
  .milestone-confetti span:nth-child(11) { left: 93%; background: var(--accent-soft); animation-delay: .09s; }
  .milestone-confetti span:nth-child(12) { left: 58%; background: var(--accent2-soft); animation-delay: .14s; }

  @keyframes confettiFall {
    0%   { opacity: 1; transform: translateY(0) rotate(0deg); }
    100% { opacity: 0; transform: translateY(80px) rotate(300deg); }
  }

  .sw-update-banner {
    position: fixed;
    left: 50%;
    bottom: 20px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--paper-raised);
    color: var(--ink);
    box-shadow: 0 6px 20px var(--shadow);
    font-family: "Gowun Dodum", "Malgun Gothic", sans-serif;
    font-size: 0.9rem;
    z-index: 1000;
  }

  .sw-update-banner__btn {
    font-family: inherit;
    font-size: 0.85rem;
    padding: 6px 12px;
    border: 1px solid var(--accent);
    border-radius: 8px;
    background: transparent;
    color: var(--accent);
    cursor: pointer;
  }

  .sw-update-banner__btn:hover {
    background: var(--accent);
    color: var(--paper);
  }
