/* Theme 2 v2: 飞象式轻拟物浅色视觉（用户端专用）
   Theme 1 remains in each page. 切换主题时仅互换底部 link 的注释。 */
:root{
  --blue:#2C2D30!important;--blue-deep:#000!important;--blue-soft:#F0F1F3!important;
  --deep:#2C2D30!important;--primary:#2C2D30!important;--primary2:#2C2D30!important;
  --ink:#2C2D30!important;--muted:#6B7280!important;--line:#E8E9EC!important;
  --bg:#F7F8FA!important;--card:#FFFFFF!important;--soft:#F0F1F3!important;
}
html,body{background:#F7F8FA!important;color:#2C2D30!important}
body{font-family:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",system-ui,sans-serif!important;letter-spacing:.01em}
body:before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(circle at 10% 10%,#FFE5EC 0,transparent 27%),radial-gradient(circle at 90% 16%,#FFF5E6 0,transparent 28%),radial-gradient(circle at 48% 100%,#E0F4FF 0,transparent 34%);opacity:.72}

/* shared header */
.hh-topbar,.topbar{background:#FFFFFFE8!important;border-bottom:1px solid #E8E9EC!important;backdrop-filter:blur(16px)!important}
.hh-brand,.brand{color:#2C2D30!important}.hh-brand small,.brand small{color:#9CA3AF!important}
.hh-logo,.logo{color:#2C2D30!important;background:#FFFFFF!important;border:1px solid #E8E9EC!important;box-shadow:0 5px 14px #00000012!important;white-space:nowrap;font-size:11px!important;letter-spacing:-.04em}
.hh-nav,.topnav{color:#6B7280!important}.hh-nav a:hover,.hh-nav a.active,.topnav a:hover,.topnav a.active{color:#2C2D30!important;border-bottom-color:#2C2D30!important}
.hh-actions{gap:8px!important}.hh-login,.login{color:#fff!important;background:#2C2D30!important;border-color:#2C2D30!important;box-shadow:0 7px 14px #FF4D6D4D!important}.hh-login:hover,.login:hover{background:#E63E5C!important}
.hh-profile,.hh-admin,.profile-shortcut{color:#2C2D30!important;background:#FFFFFF!important;border-color:#E8E9EC!important}
.hh-logout,.logout{color:#E63E5C!important;background:#FFFFFF!important;border-color:#FFD3DC!important}

/* cards and controls */
.panel,.card,.summary-card,.filters,.service-card,.page-contact-card,.page-contact-box,.hh-contact-card,.hh-pay-box,.hh-filter-capsule{background:#FFFFFF!important;border-color:#E8E9EC!important;color:#2C2D30!important;box-shadow:0 12px 30px #0000000d!important}
.panel h2,.panel h3,.card h2,.card h3,.summary-card h2,.page-contact-card h2,.page-contact-box h3{color:#2C2D30!important}
.panel p,.card p,.meta,.sub,.desc,.work{color:#9CA3AF!important}
.control,.search-box input{color:#2C2D30!important;background:#FFFFFF!important;border-color:#E8E9EC!important;border-radius:8px!important}.control:focus,.search-box input:focus{border-color:#2C2D30!important;box-shadow:0 0 0 4px #FF4D6D1a!important}
.btn,.submit,.btn.primary,.renew,.cta,.search-box button,.banner-search button{color:#fff!important;background:#2C2D30!important;border-color:#2C2D30!important;border-radius:11px!important;box-shadow:0 7px 15px #FF4D6D33!important}.btn:hover,.submit:hover,.renew:hover,.cta:hover,.search-box button:hover,.banner-search button:hover{background:#E63E5C!important}
.chip,.grade-chip,.tabs{background:#FFFFFF!important;border-color:#E8E9EC!important;color:#6B7280!important}.chip.active,.grade-chip.active,.tabs button.active{color:#fff!important;background:#2C2D30!important;border-color:#2C2D30!important}
.cw-card{background:#FFFFFF!important;border-color:#E8E9EC!important;border-radius:16px!important;box-shadow:0 2px 8px #0000000f!important}.cw-card:hover{border-color:#FF6B85!important;box-shadow:0 16px 28px #00000016!important;transform:translateY(-4px)}.cw-cover,.cw-cover-placeholder{border-radius:16px 16px 0 0}.cw-cover-placeholder{background:linear-gradient(135deg,#FFE5EC,#E0F4FF)!important;color:#2C2D30!important}
#grid{gap:24px!important}.card{background:#FFFFFF!important;border:1px solid #E8E9EC!important;border-radius:16px!important;box-shadow:0 2px 8px #0000000f!important}.card:hover{border-color:#FF6B85!important;box-shadow:0 16px 28px #00000016!important;transform:translateY(-4px)}.card .cover,.card .cover-ph{border-radius:16px 16px 0 0}.card .cover-ph{background:linear-gradient(135deg,#FFE5EC,#E0F4FF)!important;color:#2C2D30!important}.card .body{background:#FFFFFF!important}.card h2{color:#2C2D30!important}.card .desc{color:#9CA3AF!important}.member{background:#FFD93D!important;color:#8B6914!important}
.tag.subject{color:#FF4D6D!important;background:#FFF0F3!important}.member-badge{color:#8B6914!important;background:#FFD93D!important}.tag.grade{color:#6B7280!important;background:#F0F4F8!important}.tag.vol{color:#6B7280!important;background:#F0F4F8!important}.pin-badge{background:#FFD93D!important;color:#8B6914!important}

/* homepage hero：移除巨型圆角框，保留开放式首屏与浮动搜索舱 */
.banner{margin-top:18px!important;min-height:240px!important;padding:44px 48px!important;position:relative!important;overflow:hidden!important;background:#FFFFFF99!important;border:1px solid #E8E9EC!important;color:#2C2D30!important;box-shadow:0 10px 28px #0000000b!important;border-radius:20px!important}
.banner:before{width:180px!important;height:180px!important;right:8%!important;top:8px!important;border:0!important;background:radial-gradient(circle,#FF4D6D55 0,#FF4D6D00 70%)!important;filter:blur(2px)!important}.banner:after{width:220px!important;height:220px!important;right:2%!important;bottom:-16px!important;border:0!important;background:radial-gradient(circle,#FFE5EC66 0,#FFE5EC00 70%)!important}
.banner-copy{max-width:820px!important;margin:0 auto!important;position:relative!important;z-index:1!important}.banner-kicker{display:inline-flex!important;align-items:center!important;gap:9px!important;margin:0 0 15px!important;padding:7px 13px!important;border:1px solid #FFD3DC!important;border-radius:999px!important;background:#FFFFFF99!important;color:#6B7280!important;font-size:11px!important;letter-spacing:.14em!important}.banner-kicker:before{content:"✦";font-size:13px;color:#FF4D6D}.banner h1{color:#2C2D30!important;font-size:clamp(28px,4.2vw,48px)!important;letter-spacing:-.06em!important}.banner p{color:#6B7280!important;max-width:680px!important}.banner-search{max-width:650px!important;margin-top:28px!important;padding:7px!important;border:1px solid #ffffff!important;border-radius:18px!important;background:linear-gradient(135deg,#2C2D30,#FFFFFF)!important;box-shadow:0 15px 30px #FF4D6D332224,0 0 0 5px #ffffff99!important}.banner-search input{color:#2C2D30!important;background:transparent!important;padding:13px 16px!important}.banner-search input::placeholder{color:#C4C9D1!important}.banner-search button{min-height:44px!important;border-radius:8px!important;background:#FF4D6D!important;color:#fff!important;box-shadow:none!important}.banner-search button:hover{background:#E63E5C!important}
.benefit-card{background:linear-gradient(120deg,#FFE5EC,#FFF5E6 56%,#FF4D6D)!important;color:#2C2D30!important;box-shadow:0 16px 30px #FF4D6D2b!important;border-radius:24px!important}.benefit-card .sub{color:#6B7280!important}

/* 登录 / 注册弹窗 */
.auth-modal{background:#FFFFFF!important;border:1px solid #E8E9EC!important;box-shadow:0 26px 64px #00000024!important}
.auth-side{background:linear-gradient(150deg,#FFE5EC 0%,#FFF5E6 58%,#FF4D6D 100%)!important;color:#2C2D30!important}
.auth-side:before,.auth-side:after{border-color:#ffffff80!important}.auth-logo{background:#FFFFFF99!important;color:#2C2D30!important;box-shadow:0 8px 18px #0000001c!important}.auth-side h3{color:#2C2D30!important}.auth-side p,.auth-feats li{color:#6B7280!important}.auth-feats li:before{color:#2C2D30!important;background:#FFFFFF99!important}
.auth-main{background:#FFFFFF!important}.auth-main h2{color:#2C2D30!important}.auth-sub,.auth-links{color:#9CA3AF!important}.auth-tabs{background:#F0F1F3!important}.auth-tabs button{color:#9CA3AF!important}.auth-tabs button.active{color:#fff!important;background:#2C2D30!important;box-shadow:0 5px 12px #FF4D6D4D!important}.auth-main .field label{color:#6B7280!important}.auth-main .field input{background:#fff!important;border-color:#E8E9EC!important;color:#2C2D30!important}.auth-main .field input:focus{border-color:#2C2D30!important;box-shadow:0 0 0 4px #FF4D6D1a!important}.auth-main .submit{background:#2C2D30!important;box-shadow:0 7px 15px #FF4D6D33!important}.auth-links a,.privacy-link{color:#FF4D6D!important}.privacy-consent{color:#9CA3AF!important}.privacy-consent input{accent-color:#2C2D30!important}.auth-top .close{background:#F0F1F3!important;color:#6B7280!important}.auth-top .close:hover{background:#E8E9EC!important;color:#2C2D30!important}
.privacy-modal{background:#FFFFFF!important}.privacy-head,.privacy-footer{border-color:#E8E9EC!important}.privacy-body{color:#6B7280!important}.privacy-body h3{color:#2C2D30!important}.privacy-footer button{background:#2C2D30!important}

/* contact widget */
.hh-contact-service,.service-fab-btn{background:#2C2D30!important}.hh-contact-follow{background:#FF4D6D!important}.hh-contact-card:after{background:#FFFFFF!important;border-color:#E8E9EC!important}.hh-contact-card h3,.hh-pay-box h2{color:#2C2D30!important}.hh-contact-card p,.hh-pay-box p{color:#9CA3AF!important}

/* 用户端侧边导航与历史记录 */
body:has(.shell){padding-top:0!important}
.hh-actions .hh-logout{display:none!important}.hh-profile-page .hh-actions .hh-logout{display:flex!important}
.hh-actions .hh-admin{display:none!important}.hh-nav .hh-admin{display:block!important}
body>.hh-actions{position:absolute!important;top:17px!important;right:25px!important}
body:has(.hh-topbar){padding-left:228px!important;transition:padding-left .42s cubic-bezier(.22,.61,.36,1)!important}
body:has(.hh-actions #authAction[hidden]){padding-top:0!important}
body:has(.hh-topbar) .shell{padding-top:70px!important}
.hh-topbar{position:fixed!important;left:0!important;top:0!important;width:228px!important;height:100vh!important;min-height:100vh!important;z-index:70!important;display:flex!important;flex-direction:column!important;align-items:stretch!important;justify-content:flex-start!important;padding:22px 14px!important;border-right:1px solid #E8E9EC!important;border-bottom:0!important;background:#FFFFFFF2!important;backdrop-filter:blur(18px)!important;transition:width .42s cubic-bezier(.22,.61,.36,1),padding .42s cubic-bezier(.22,.61,.36,1)!important}
.hh-brand{width:100%!important;padding:2px 8px 22px!important;gap:10px!important}.hh-logo{width:34px!important;height:34px!important}.hh-brand strong{font-size:15px!important;font-weight:700!important}.hh-brand small{font-size:8px!important}
.hh-nav{position:static!important;left:auto!important;transform:none!important;display:flex!important;flex-direction:column!important;align-items:stretch!important;gap:4px!important;width:100%!important;color:#6B7280!important;font-size:13px!important}.hh-nav a{padding:11px 12px!important;border:0!important;border-radius:10px!important;display:flex!important;align-items:center!important;font-weight:500!important}.hh-nav a:hover,.hh-nav a.active{color:#2C2D30!important;background:#FFF0F3!important;border:0!important}
.hh-sidebar-history{display:flex!important;flex:0 1 230px!important;flex-direction:column!important;margin:18px 2px 10px!important;padding:15px 10px 12px!important;border:1px solid #E8E9EC!important;border-radius:12px!important;background:#FFFFFF99!important;min-height:96px!important;overflow:hidden!important;visibility:visible!important;opacity:1!important;transition:opacity .18s ease .26s,visibility 0s linear .26s!important}.hh-history-title{margin:0 0 10px!important;color:#6B7280!important;font-size:11px!important;font-weight:700!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important}.hh-history-list{display:flex!important;flex:1!important;min-height:0!important;flex-direction:column!important;gap:2px!important;overflow-y:auto!important;scrollbar-width:thin!important}.hh-history-list a{display:block!important;overflow:hidden!important;padding:7px!important;border-radius:7px!important;color:#6B7280!important;text-decoration:none!important;font-size:11px!important;white-space:nowrap!important;text-overflow:ellipsis!important}.hh-history-list a:hover{background:#FFF0F3!important;color:#2C2D30!important}.hh-history-empty{display:block!important;padding:8px 7px!important;color:#9CA3AF!important;font-size:11px!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important}
.hh-actions{position:absolute!important;top:17px!important;right:25px!important;z-index:250!important;min-width:0!important;width:118px!important;display:flex!important;flex-direction:column!important;align-items:stretch!important;gap:6px!important}.hh-actions .hh-profile{display:none!important}.hh-actions .hh-admin,.hh-actions .hh-login,.hh-actions .hh-logout{width:100%!important;text-align:center!important;justify-content:center!important;white-space:nowrap!important}.hh-actions .hh-login{min-height:38px!important}body:has(.hh-actions #authAction:not([hidden])){padding-top:0!important}.hh-profile-card{display:flex!important;flex:0 0 auto!important;align-items:center!important;width:100%!important;margin-top:auto!important;padding:10px 9px!important;border:1px solid #E8E9EC!important;border-radius:12px!important;background:#FFF0F3!important;color:#2C2D30!important;text-align:left!important;cursor:pointer!important;transition:background .18s,box-shadow .18s!important}.hh-profile-card:hover{background:#FFF0F3!important;box-shadow:0 6px 16px #0000000d!important}.hh-profile-avatar{display:grid!important;place-items:center!important;width:32px!important;height:32px!important;flex:0 0 32px!important;border-radius:50%!important;background:#FF4D6D!important;color:#fff!important;font-size:14px!important;font-weight:800!important}.hh-profile-card-copy{display:flex!important;flex-direction:column!important;min-width:0!important;margin-left:8px!important}.hh-profile-card-copy strong{overflow:hidden!important;font-size:11px!important;white-space:nowrap!important;text-overflow:ellipsis!important}.hh-profile-card-copy small{margin-top:3px!important;color:#9CA3AF!important;font-size:9px!important;white-space:nowrap!important}.hh-profile-card-arrow{margin-left:auto!important;color:#9CA3AF!important;font-size:18px!important;line-height:1!important}
.modal-bg{z-index:300!important}
.hh-login-overlay{position:fixed!important;inset:0!important;z-index:500!important;display:grid!important;place-items:center!important;padding:20px!important;background:#2C2D3073!important;backdrop-filter:blur(4px)!important}.hh-login-overlay-box{position:relative!important;width:min(840px,100%)!important;height:min(680px,calc(100vh - 40px))!important;overflow:hidden!important;border-radius:18px!important;background:#FFFFFF!important;box-shadow:0 28px 70px #2C2D3033!important}.hh-login-overlay-box iframe{display:block!important;width:100%!important;height:100%!important;border:0!important;background:#F7F8FA!important}.hh-login-overlay-close{position:absolute!important;right:12px!important;top:10px!important;z-index:2!important;width:32px!important;height:32px!important;border:0!important;border-radius:50%!important;background:#2C2D30CC!important;color:#fff!important;font-size:22px!important;line-height:1!important;cursor:pointer!important}.hh-login-overlay-close:hover{background:#000!important}
.hh-login-dialog-box{width:min(420px,100%)!important;height:auto!important;padding:34px 30px!important}.hh-login-dialog-inner{display:flex!important;flex-direction:column!important;gap:12px!important}.hh-login-dialog-inner h2{margin:0 0 4px!important;color:#2C2D30!important;font-size:24px!important}.hh-login-dialog-tabs{display:flex!important;gap:8px!important}.hh-login-dialog-tabs button{border:0!important;background:transparent!important;color:#6B7280!important;padding:6px 12px!important;cursor:pointer!important}.hh-login-dialog-tabs button.active{border-bottom:2px solid #FFD93D!important;color:#2C2D30!important}.hh-login-input{width:100%!important;padding:12px 14px!important;border:1px solid #E8E9EC!important;border-radius:10px!important;background:#fff!important;font:inherit!important}.hh-login-agree{font-size:12px!important;color:#6B7280!important}.hh-login-submit{padding:12px!important;border:0!important;border-radius:10px!important;background:#2C2D30!important;color:#fff!important;font-weight:700!important;cursor:pointer!important}.hh-login-submit:disabled{opacity:.55!important}.hh-login-error{min-height:18px!important;margin:0!important;color:#E63E5C!important;font-size:12px!important}
.shell>.sidebar{background:#FFFFFF!important;border-right-color:#E8E9EC!important;color:#2C2D30!important}.shell>.sidebar .brand{display:none!important}.shell>.sidebar .brand{color:#2C2D30!important}.shell>.sidebar .brand small{color:#9CA3AF!important}.shell>.sidebar .nav-item{color:#6B7280!important;background:transparent!important}.shell>.sidebar .nav-item:hover,.shell>.sidebar .nav-item.active{color:#2C2D30!important;background:#FFF0F3!important;box-shadow:none!important}.shell>.sidebar .side-note{color:#9CA3AF!important;background:#FFF7E0!important;border-color:#FFE1A3!important}.shell>.workspace{background:#F7F8FA!important}.shell .panel,.shell .table-wrap,.shell .modal{background:#FFFFFF!important;border-color:#E8E9EC!important;color:#2C2D30!important}.shell .panel-head h2,.shell .panel-head p,.shell label{color:#2C2D30!important}.shell .control{background:#fff!important;border-color:#E8E9EC!important;color:#2C2D30!important}.shell .btn.primary{background:#2C2D30!important;color:#fff!important}.shell .drop{background:#F7F8FA!important;border-color:#E8E9EC!important}.shell .drop:before{background:#FFD93D!important}
.hh-sidebar-toggle{display:grid!important;place-items:center!important;width:30px!important;height:30px!important;margin:0 0 12px auto!important;padding:6px!important;border:1px solid #E8E9EC!important;border-radius:8px!important;background:#FFFFFF!important;color:#6B7280!important;cursor:pointer!important}.hh-sidebar-toggle svg{display:block!important;width:17px!important;height:17px!important;fill:none!important;stroke:currentColor!important;stroke-width:1.6!important;stroke-linecap:round!important;stroke-linejoin:round!important}.hh-sidebar-toggle:hover{color:#2C2D30!important;background:#FFF0F3!important}
body:has(.hh-topbar).hh-sidebar-collapsed{padding-left:78px!important}.hh-sidebar-collapsed .hh-topbar{width:78px!important;padding-left:12px!important;padding-right:12px!important}.hh-sidebar-collapsed .hh-brand{justify-content:center!important;padding-left:0!important;padding-right:0!important}.hh-sidebar-collapsed .hh-brand>span:not(.hh-logo){display:none!important}.hh-sidebar-collapsed .hh-logo{margin:auto!important}.hh-sidebar-collapsed .hh-nav{display:none!important}.hh-sidebar-collapsed .hh-sidebar-history{flex:0 0 0!important;min-height:0!important;margin:0!important;padding:0!important;border:0!important;visibility:hidden!important;opacity:0!important;transition:none!important}.hh-sidebar-collapsed .hh-profile-card{justify-content:center!important;padding-left:5px!important;padding-right:5px!important}.hh-sidebar-collapsed .hh-profile-card-copy,.hh-sidebar-collapsed .hh-profile-card-arrow{display:none!important}.hh-sidebar-collapsed .hh-sidebar-toggle{margin-right:auto!important;margin-left:auto!important}
.hh-brand>span:not(.hh-logo),.hh-nav a,.hh-profile-card-copy,.hh-profile-card-arrow{white-space:nowrap!important;overflow:hidden!important;transition:opacity .42s ease,font-size .42s ease,max-width .42s ease!important}.hh-nav a{min-width:0!important}
.hh-sidebar-collapsed .hh-brand>span:not(.hh-logo){display:block!important;max-width:0!important;opacity:0!important}.hh-sidebar-collapsed .hh-profile-card-copy{display:flex!important;max-width:0!important;opacity:0!important}.hh-sidebar-collapsed .hh-profile-card-arrow{display:block!important;max-width:0!important;opacity:0!important}
@media(max-width:760px){body:has(.hh-topbar){padding-left:0!important}body:has(.hh-actions #authAction:not([hidden])){padding-top:0!important}body:has(.hh-topbar) .shell{padding-top:60px!important}.hh-topbar{position:sticky!important;left:auto!important;top:0!important;width:100%!important;height:60px!important;min-height:60px!important;display:flex!important;flex-direction:row!important;align-items:center!important;padding:0 13px!important;border-right:0!important;border-bottom:1px solid #E8E9EC!important}.hh-brand{padding:0!important;width:auto!important}.hh-nav,.hh-sidebar-history,.hh-profile-card{display:none!important}.hh-actions{position:fixed!important;top:13px!important;right:13px!important;width:auto!important;margin:0!important;display:flex!important;flex-direction:row!important;align-items:center!important;gap:6px!important}.hh-actions .hh-admin,.hh-actions .hh-logout{display:none!important}.hh-actions .hh-login{width:auto!important}.hh-brand strong{font-size:14px!important}body>.hh-actions{position:fixed!important;top:13px!important;right:13px!important}}
@media(max-width:760px){.hh-sidebar-toggle{display:none!important}.hh-sidebar-collapsed .hh-topbar{width:100%!important;padding-left:13px!important;padding-right:13px!important}.hh-sidebar-collapsed .hh-brand>span:not(.hh-logo){display:inline!important}.hh-sidebar-collapsed .hh-brand{justify-content:flex-start!important}.hh-sidebar-collapsed .hh-logo{margin:0!important}body:has(.hh-topbar).hh-sidebar-collapsed{padding-left:0!important}}

/* profile user page */
.content,.side{color:#2C2D30!important}.side{background:#FFFFFF!important;border-color:#E8E9EC!important;box-shadow:0 10px 28px #0000000a!important}.nav-btn{color:#6B7280!important}.nav-btn:hover,.nav-btn.active{color:#2C2D30!important;background:#FFF0F3!important}.summary-card.main{background:linear-gradient(135deg,#FFE5EC,#FFD3DC)!important;color:#2C2D30!important}.summary-card.main .label,.summary-card.main .value{color:#2C2D30!important}.summary-card.main .sub{color:#6B7280!important}.redeem-panel{background:#FFF7E0!important;border-color:#FFE1A3!important}.redeem-icon{background:#FFD93D!important;color:#8B6914!important}.support-icon{background:#2C2D30!important}
body.hh-profile-page .container{padding-top:56px!important;padding-bottom:72px!important}body.hh-profile-page .layout{gap:26px!important}body.hh-profile-page .panel{margin-bottom:24px!important;padding:26px!important}body.hh-profile-page .summary{gap:18px!important}body.hh-profile-page .summary-card{padding:21px!important}body.hh-profile-page .overview-tools{gap:20px!important;margin-top:22px!important}body.hh-profile-page .layout>.side{margin-bottom:58px!important}
body:has(.shell){padding-top:0!important}.shell{margin-top:0!important}.shell>.sidebar{margin-top:0!important}body:has(.shell)>.hh-actions{display:none!important}
body:has(.shell) .shell{padding-top:0!important;margin-top:0!important}
.shell{display:flex!important;flex-direction:column!important;width:100%!important;height:100vh!important;min-height:0!important;overflow:hidden!important}.shell>.sidebar{position:relative!important;z-index:25!important;display:flex!important;flex:0 0 64px!important;width:100%!important;height:64px!important;min-height:64px!important;align-items:center!important;padding:8px 22px!important}.shell>.sidebar .nav{min-width:0!important;overflow-x:auto!important;scrollbar-width:none!important}.shell>.sidebar .nav::-webkit-scrollbar{display:none!important}.shell>.workspace{flex:1!important;width:100%!important;height:auto!important;min-height:0!important;overflow-y:auto!important}.shell>.workspace>.topbar{top:0!important}
@media(min-width:761px){.layout{display:block!important}.layout>.side{display:flex!important;align-items:center!important;gap:6px!important;width:100%!important;margin:0 0 20px!important;padding:10px!important;border-radius:14px!important}.layout>.side .side-title{margin:0 10px 0 4px!important;white-space:nowrap!important}.layout>.side .nav-btn{width:auto!important;padding:9px 14px!important;white-space:nowrap!important}.layout>.side .nav-btn.logout{margin-left:auto!important}.layout>.content{width:100%!important}}
.privacy-body,.agreement{color:#6B7280!important}.privacy-body h3,.agreement h3{color:#2C2D30!important}
.purchase-state{font-weight:700}.purchase-state.active{color:#2E9E5B}.purchase-state.expired{color:#E63E5C}.open-link[aria-disabled="true"]{color:#9CA3AF!important;pointer-events:none}

/* viewer */
.toolbar{background:#FFFFFF!important;border-color:#E8E9EC!important}.frame-wrap{background:#FFFFFF!important;border-color:#E8E9EC!important}.preview-tip{background:#FFF7E0!important;color:#8B6914!important}
@media(max-width:760px){.hh-topbar,.topbar{background:#FFFFFFF5!important}.banner{padding:30px 22px 26px!important;min-height:0!important;border-radius:16px!important}.banner h1{font-size:30px!important}.banner-search{border-radius:15px!important}.banner-search button{min-height:44px!important;padding:0 18px!important}.courseware-grid{gap:12px!important}.cw-card{border-radius:16px!important}}

/* 跨页面与窄屏稳定性：固定滚动条槽，限制主体和弹窗溢出。 */
html{scrollbar-gutter:stable!important}
body:has(.hh-topbar){min-width:0!important;overflow-x:hidden!important;overflow-x:clip!important}
body:has(.hh-topbar)>main,body:has(.hh-topbar)>.container,body:has(.hh-topbar)>.contact-main{min-width:0!important}
.modal-bg.open{align-items:center!important;overflow-y:auto!important}.modal-bg.open>.modal{max-height:calc(100dvh - 40px)!important;overflow-y:auto!important}
@media(max-width:760px){.shell{height:auto!important;min-height:100vh!important;overflow:visible!important}.shell>.sidebar{position:sticky!important;top:60px!important;height:auto!important;min-height:56px!important;flex-basis:auto!important;padding:8px 12px!important}.shell>.workspace{overflow:visible!important}.shell>.sidebar .nav-item{flex:0 0 auto!important}.hh-actions{max-width:calc(100vw - 26px)!important}.modal-bg.open{padding:12px!important}.modal-bg.open>.modal{max-height:calc(100dvh - 24px)!important}}
/* SPA 已原地替换主体，整页淡出会让固定侧栏也闪一下，因此关闭旧的整页动画。 */
body.hh-page-ready,body.hh-page-leaving{animation:none!important}

/* ==========================================================================
   theme3 增量覆盖：页面内联样式里未被主题层覆盖的硬编码色（设计稿珊瑚红方案）
   这些选择器在页面 <style> 里写死了旧色值，必须在主题层显式盖掉。
   ========================================================================== */

/* 令牌补全：设计稿 secondary / 语义色 */
:root{
  --green:#6BCB77!important;--red:#E63E5C!important;--gold:#E6A817!important;
  --orange:#FF8C42!important;--primary:#FF4D6D!important;--primary2:#FF4D6D!important;
  --surface:#FFFFFF!important;--blue-light:#FF6B85!important;--purple:#9B59B6!important;
}

/* 课程网格：设计稿 3 列 / 24px 间距 */
.courseware-grid{gap:24px!important}
@media(max-width:1100px){.courseware-grid{gap:20px!important}}

/* --------------------------------------------------------------------------
   ⚠️ 2026-09-21 第九轮：列数从「写死 3 列」改成 auto-fill 自适应。
   起因：`--hh-content` 放宽到 2080px 之后，写死的 3 列在大屏上被拉成巨幅卡片 ——
   2560 下实测单卡 663px 宽（16:9 封面高 373px），一张卡占掉半个屏幕，
   内容看着「铺得太开」，和用户抱怨的「边距大大」是同一个观感问题。

   为什么选 minmax(320px,1fr) 这个数：列数 = floor((内容内宽 + 24) / (320 + 24))。
     · 内容内宽 1148（1440 视口，即定稿宽度）→ floor(1172/344) = 3 列，单卡 367px
       —— **与改造前一模一样，定稿及以下视口零变化**；
     · 内容内宽 1494（1920 视口）→ 4 列，单卡 355px；
     · 内容内宽 2032（2560 视口）→ 5 列，单卡 387px。
   即单卡宽度全程锁在 320–406px 区间，不会在任何一个分辨率上被拉爆。

   覆盖范围：`#grid` 是 coursewares.html 及其 4 个派生分类页 + ppt-library.html 的网格
   （6 个页面全部是 `<section class="grid" id="grid">`，用 id 而不用类名是为了**避开
   admin.html 的同名 `.grid` 布局包裹层**，见上面 MAP3 那条的注释），
   `.courseware-grid` 是首页，`.index-courseware-grid` 是个人中心。
   三者的写死值都在**页面内联 <style>** 里（无 !important），所以这里
   带 !important 的覆盖必胜。小屏方向不受影响：responsive.css 的
   `max-width:980px → repeat(2,…)` 与 `max-width:760px → 1fr!important`
   前者无 !important（被本条压掉，但 761–980px 的内容内宽 < 664px，
   auto-fill 本来就只给 1 列，结果一致）；后者与 auto-fill 同为 !important、
   同特异性 (0,1,0) vs (1,0,0)——`#grid` 更高，仍胜，但 ≤760px 内容内宽
   同样 < 664px，auto-fill 结果也是 1 列。三档实测见验证记录。
   -------------------------------------------------------------------------- */
#grid,.courseware-grid,.index-courseware-grid{
  grid-template-columns:repeat(auto-fill,minmax(320px,1fr))!important;
}

/* 封面 */
.cw-cover,.cover,.course-card img,.thumb{background:#F0F4F8!important}
.cw-cover-placeholder,.cover-ph{background:linear-gradient(135deg,#FF4D6D,#FF8FA3)!important;color:#fff!important}

/* 标签：设计稿圆角 6px / 12px 字 / 500 字重 */
.tag{border-radius:6px!important;padding:4px 10px!important;font-size:12px!important;font-weight:500!important}
.tag.vol,.tag.volume,.tag.grade{color:#6B7280!important;background:#F0F4F8!important}
.tag.subject{color:#FF4D6D!important;background:#FFF0F3!important}

/* 空态 / 状态行 / 页脚 */
.empty,.status,.cw-status{color:#9CA3AF!important}
.footer{border-top-color:#E8E9EC!important;color:#9CA3AF!important}
.footer-row,.footer-copy{border-top-color:#E8E9EC!important;color:#9CA3AF!important}
.footer-link{color:#6B7280!important}

/* 弹窗遮罩、关闭按钮、提示 */
.modal-bg{background:#2C2D3066!important}
.close{color:#9CA3AF!important}
.hint{color:#9CA3AF!important}
.toast{background:#2C2D30!important;color:#fff!important;border-radius:8px!important}
.logout-icon{color:#E63E5C!important;background:#FFF0F3!important}
.logout-cancel{color:#6B7280!important;background:#fff!important;border-color:#E8E9EC!important}
.logout-cancel:hover{border-color:#C4C9D1!important;background:#F7F8FA!important}
.logout-confirm{background:#E63E5C!important;box-shadow:0 4px 12px #E63E5C33!important}
.logout-confirm:hover{background:#C9304C!important}
.field input:focus,.control:focus{border-color:#FF4D6D!important;box-shadow:0 0 0 3px #FF4D6D1a!important}
.password-toggle{color:#9CA3AF!important}
.privacy-body{color:#6B7280!important}

/* coursewares：筛选区虚线分隔 / 浮层 / 置顶徽标 */
.filter-body,.filter-groups .filter-row+.filter-row{border-top-color:#F0F1F3!important}
.filter-search{border-left-color:#F0F1F3!important;border-top-color:#F0F1F3!important}
.hh-filter-capsule::after{background:linear-gradient(180deg,#2C2D3014,#2C2D3000)!important}
.hh-filter-panel>.filters{box-shadow:0 12px 32px #00000014!important;border-radius:0 0 16px 16px!important}
.hh-filter-capsule .cap-value{background:#FF4D6D!important;color:#fff!important}
.pin,.pin-badge{background:#FF4D6D!important;color:#fff!important;box-shadow:0 2px 6px #FF4D6D4d!important}
.member{background:#FFD93D!important;color:#8B6914!important}

/* 会员页 */
.rule{background:#F7F8FA!important}
.benefit{border-color:#FFFFFF99!important;background:#FFFFFF80!important}

/* 联系页 */
.hero{border-color:#FFD3DC!important;background:linear-gradient(115deg,#FFFFFF,#FFF0F3)!important}
.hero-tags span{border-color:#FFD3DC!important;color:#6B7280!important;background:#fff!important}
.service-item{border-color:#E8E9EC!important;background:#fff!important}
.service-icon{color:#FF4D6D!important;background:#FFF0F3!important}
.page-contact-box button{background:#FF4D6D!important;color:#fff!important}

/* 个人中心 */
.side-title{color:#9CA3AF!important}
.support-link{border-color:#FFD3DC!important;background:#FFF9FA!important}
.status,.current-tag{color:#2C2D30!important;background:#6BCB77!important}
.status.expired{color:#FF4D6D!important;background:#FFE4E8!important}
.purchase-state.active{color:#2E9E5B!important}
.purchase-state.expired{color:#E63E5C!important}
.revoke{border-color:#FFD3DC!important;color:#E63E5C!important;background:#fff!important}
.revoke:hover{background:#FFF0F3!important}
.redeem-panel{border-color:#FFD3DC!important;background:#FFF9FA!important}

/* 移动端底部 dock */
.mobile-dock{border-color:#E8E9EC!important;background:#FFFFFFF5!important;box-shadow:0 12px 32px #00000012!important}
.dock-item{color:#6B7280!important}
.dock-item.active{color:#FF4D6D!important;background:#FFF0F3!important}

/* viewer */
html,body{background:#F7F8FA!important;color:#2C2D30!important}
.toolbar{border-bottom-color:#E8E9EC!important}
.toolbar button{border-color:#E8E9EC!important;color:#FF4D6D!important;background:#fff!important}
.watermarks span{color:#FF4D6D2E!important}
.loading,.blocked{background:#F7F8FA!important;color:#6B7280!important}
.blocked,.blocked strong{color:#E63E5C!important}
.loading-spinner{border-color:#FFD3DC!important;border-top-color:#FF4D6D!important}
.preview-tip{border-bottom-color:#FFE1A3!important}

/* ==========================================================================
   theme3 权威修正：主色令牌与关键组件（置于最末，确保同特异性下胜出）
   ========================================================================== */
:root{
  --blue:#FF4D6D!important;--blue-deep:#E63E5C!important;--blue-soft:#FFF0F3!important;
  --blue-light:#FF6B85!important;--deep:#2C2D30!important;
  --primary:#FF4D6D!important;--primary2:#FF4D6D!important;
  --ink:#2C2D30!important;--muted:#6B7280!important;--line:#E8E9EC!important;
  --bg:#F7F8FA!important;--card:#FFFFFF!important;--soft:#F0F1F3!important;
}

/* 主按钮：珊瑚红底 + 8px 圆角 + 设计稿按钮阴影 */
.btn,.submit,.btn.primary,.renew,.cta,.search-box button,.banner-search button,
.redeem button,.btn.secondary,.pager button.active{
  background:#FF4D6D!important;border-color:#FF4D6D!important;color:#fff!important;
  border-radius:8px!important;box-shadow:0 4px 12px #FF4D6D4d!important;
}
.btn:hover,.submit:hover,.renew:hover,.cta:hover,.search-box button:hover,
.banner-search button:hover,.redeem button:hover{background:#E63E5C!important;border-color:#E63E5C!important}

/* 登录 / 注册按钮 */
.hh-login,.login{
  background:#FF4D6D!important;border-color:#FF4D6D!important;color:#fff!important;
  border-radius:8px!important;box-shadow:0 4px 12px #FF4D6D4d!important;
}
.hh-login:hover,.login:hover{background:#E63E5C!important;border-color:#E63E5C!important}

/* 品牌标：珊瑚渐变方块 + 白字 */
.hh-logo,.logo{
  background:linear-gradient(135deg,#FF4D6D,#FF8FA3)!important;color:#fff!important;
  border:0!important;box-shadow:0 4px 12px #FF4D6D4d!important;
}

/* 侧栏导航：hover 浅粉，激活为主色（设计稿 interactions） */
.hh-nav a:hover{color:#2C2D30!important;background:#FFF0F3!important}
.hh-nav a.active{color:#fff!important;background:#FF4D6D!important}

/* 卡片 hover：设计稿阴影 + 上浮 4px */
.cw-card:hover,.card:hover{
  border-color:#FFB3C1!important;box-shadow:0 8px 24px #0000001a!important;
  transform:translateY(-4px)!important;
}

/* Banner 底色：2026-09-20 按用户给的参考图（887×226）**逐点取色重做**。
   结构是「一条横向主渐变 + 5 块径向补丁」，横向主渐变取自参考图 y=56 那一行
   （顶栏下沿第一行，最能代表 banner 的水平走向）：
     x=2   #fbbfc8 粉   | x=150 #f7d0cc 暖粉 | x=300 #f8e5a1 奶油
     x=500 #f7e7a2 奶油 | x=600 #f3efa8 黄绿 | x=660 #aaecd1 薄荷
     x=800 #3fcdf4 青   | x=884 #4cd9f3 青
   五块径向补丁（都在参考图里量过范围）：
     ① 左下紫雾  —— ref x0~150 / y180~226，最强处 #9e8cf0（x=2,y=210）
     ② 左中白波纹 —— ref x30~90 / y165~195，参考图里是一道近白的弧形高光
     ③ 左中暖桃  —— ref x0~140 / y85~155，最强处 #fdd2be（x=120,y=90）
     ④ 右侧薄荷带 —— ref x850~887 / y110~160，最强处 #b0f1cc（x=884,y=130）
     ⑤ 底部中段淡绿 —— ref x400~620 / y160~226，参考图里中段越往下越偏黄绿
   全部用 radial/linear 叠出来，不依赖图片资源。

   ⚠️⚠️ **纵向百分比要换算，不能直接抄参考图的 y**（这个坑第一次就踩了）。
   参考图的 banner 只有 172px 高，而 `.banner` 的盒子是 **343px**（其中顶部 64px
   被 `.hh-topnav` 压住），两者的原点也不一样。换算公式（ref_y 是参考图的绝对 y）：
       y_pct = (64 + (ref_y - 54) × 1.622) / 343
   （1.622 = 279/172，即「参考图 banner 高度 → 本页 banner 可见高度」的比例）
   横向不受影响：参考图 banner 的 x 范围与本页 banner 一一对应，百分比可直接用。
   例：暖桃补丁在参考图里最强处是 ref y=112 → y_pct = (64+58×1.622)/343 = 46%，
       而不是按 172 算出来的 34%。差 12 个百分点，第一版就是这么偏的 ——
       结果桃色一路漫到顶栏下沿，左侧本该是粉色的地方变成了桃色。

   ⚠️ 顺序即层叠顺序：**写在前面的画在上面**。紫雾/白波纹必须在最前，
      否则会被后面的绿色补丁盖掉。 */
.banner{
  background-color:#F7E7A2!important;
  background-image:
    radial-gradient(10% 34% at 7% 104%,#8E74EC 0%,#A98AEE 30%,#C9ADF1 56%,#E9D8F2 80%,#FFFFFF00 100%),
    radial-gradient(8% 11% at 7% 78%,#FFFFFF 0%,#FDEEF1 50%,#FFFFFF00 100%),
    radial-gradient(16% 17% at 3% 50%,#FDD0BC 0%,#FBD7BC 42%,#FBE2B4 74%,#FFFFFF00 100%),
    radial-gradient(11% 11% at 101% 60%,#C6F2C9 0%,#A6EFD0 50%,#FFFFFF00 100%),
    radial-gradient(18% 15% at 57% 93%,#CFEBB8 0%,#D9EDC3 55%,#FFFFFF00 100%),
    /* 2026-09-21（阶段 L）：横向渐变按参考稿「样例.txt」**重新逐点校准**。
       上一版（阶段 K）的粉→桃→黄过渡带整体偏右约 5%，实测：
         参考稿 canvas x=240 已是 #f4bed6（粉在退），本站还是 #f6a6d0（满饱和粉）；
         x=300 参考稿 #f8d0cc，本站 #f5adcf；x=360 参考稿 #f8e1bd，本站 #f4c6c7。
       也就是说「粉只保持到 ~15%，15%→31% 走完粉→桃→奶油」，
       而本站是「保持到 21%，21%→33% 走完」—— 过渡带既晚又窄。
       新停止点把过渡带从 [21,33] 拉伸到 [16,31]，中间各点按比例重排。
       校验方法：渲染后取 banner 干净区（x 0..720，避开标题墨迹 y 96..318）
       与参考稿逐点比色，Δmax 应 < 12（2026-09-21 实测见 2026-09-21.md）。 */
    linear-gradient(124deg,#f9a7d1 0%,#f5a2d4 8.7%,#f3afd3 14.3%,#f4c0d6 17.5%,#f6dbc3 23.2%,#f7e5a9 32%,#f8e0a8 39.6%,#f8e39f 45.9%,#f5e69e 52.9%,#eceab0 57.9%,#d9edc3 61.7%,#aee9d0 66.1%,#79d8e2 71.2%,#51cef3 75%,#23c2f1 81.3%,#21c7f5 90.1%,#21c7f5 100%)!important;
  border:0!important;
  /* 底部再压一层很淡的投影，让整块 banner 有「浮在页面底色上」的层次。 */
  box-shadow:0 8px 24px #0000000d,inset 0 -1px 0 #FFFFFF80!important;
}
/* `.banner:before` 在旧主题里是右上角一个 340×340 的粉雾圆（含 1px 白描边）。
   参考图右上角没有这个东西 —— 那里是甜甜圈和青色渐变，多一个粉圈会脏。
   直接收掉；紫雾已经并入上面第一条 radial，不需要伪元素了。 */
.banner:before{display:none!important}

/* --------------------------------------------------------------------------
   Banner 整图背景（2026-09-21）：首页 .banner 的第一子节点 <img class="banner-bg">
   作为背景层铺满整块（原图 1920×489，object-fit:cover 居中裁切），
   标题/副标题/搜索舱（.banner-copy）保持浮在其上。
   旧的装饰元素全部收掉：装饰星（.banner-stars）与 3D 几何插画（.banner:after）
   已被整图背景取代，再画就是双份装饰。.banner:after 原本承载右侧 47% 的插画，
   这里用 display:none 收掉 —— 本块写在它原始定义之后，同特异性靠源码顺序决胜。
   banner 原有的渐变背景/背景色不再删（被不透明 <img> 整层盖住，留着当加载兜底）。
   -------------------------------------------------------------------------- */
.banner{position:relative!important;overflow:hidden!important}
.banner .banner-bg{
  position:absolute!important;inset:0!important;z-index:0!important;
  width:100%!important;height:100%!important;
  object-fit:cover!important;object-position:center!important;
  border-radius:inherit!important;
}
.banner .banner-stars{display:none!important}
.banner:after{display:none!important}
.banner .banner-copy{position:relative!important;z-index:1!important}

/* 分段控件 / 筛选胶囊的激活态 */
.tabs button.active,.chip.active,.grade-chip.active{
  background:#FF4D6D!important;border-color:#FF4D6D!important;color:#fff!important;
}
.chip:hover,.grade-chip:hover{color:#FF4D6D!important;border-color:#FF6B85!important}

/* 右下角浮层按钮（设计稿：成为会员=珊瑚，在线客服=橙） */
.hh-contact-follow{background:#FF4D6D!important}
.hh-contact-service,.service-fab-btn{background:#FF8C42!important}

/* 移动端：搜索舱圆角与按钮尺寸 */
@media(max-width:760px){
  .banner-search{border-radius:12px!important}
  .banner-search button{min-height:44px!important;padding:0 18px!important}
}

/* 侧栏底部用户卡头像（设计稿 avatarSize 32px） */
.hh-profile-avatar{background:#FF4D6D!important;color:#fff!important;border-radius:50%!important}

/* Banner 搜索舱：设计稿浅底 + 8px 圆角（此规则同时压掉旧主题的深色渐变） */
.banner-search{
  padding:6px!important;border:1px solid #E8E9EC!important;border-radius:12px!important;
  background:#FFFFFF!important;box-shadow:0 10px 24px #00000014!important;
}
.banner-search input{color:#2C2D30!important;background:transparent!important;padding:13px 16px!important}
.banner-search input::placeholder{color:#C4C9D1!important}

/* --------------------------------------------------------------------------
   `.status` 在三个页面里语义不同，必须分页面作用域，不能全局给底色：
     - coursewares.html：`.grid` 之后的状态行（"加载更多失败…"）→ 纯文字
     - membership.html：`.status` / `.status.ok` 会员状态文案 → 纯文字
     - profile.html：`.member-row` 内的有效期徽标 → 实心绿底
   全局给 background 会在 coursewares 上渲染成一条整宽绿条（已实测踩到）。
   -------------------------------------------------------------------------- */
.status{color:#9CA3AF!important;background:transparent!important}
.status.ok{color:#2E9E5B!important;background:transparent!important}
.status.expired{color:#E63E5C!important;background:transparent!important}
.member-row .status{color:#2C2D30!important;background:#6BCB77!important}
.member-row .status.expired{color:#FF4D6D!important;background:#FFE4E8!important}
.current-tag{color:#2E9E5B!important;background:transparent!important}
.device-row .current-tag{color:#2C2D30!important;background:#6BCB77!important}

/* ==========================================================================
   第二轮修正（实测驱动）：以下每条都是先在真实栈上量出偏差再补的
   ========================================================================== */

/* --------------------------------------------------------------------------
   ① 登录按钮
   header.css 用 `.hh-actions .hh-login`（特异性 0,2,1 + !important）强锁了
   字号/字重/内边距/圆角；theme3 里的 `.hh-login` 只有 (0,1,1)，压不住 →
   实测圆角仍是 7px、字号 12px、字重 800、padding 8px 13px。
   必须复用同一选择器，并放在本表最末（同特异性靠后胜出）。
   规格：components.topNav.userActions.buttons[0]
        = { borderRadius:8px, padding:8px 20px, fontSize:14px, fontWeight:500 }
   -------------------------------------------------------------------------- */
.hh-actions .hh-login,.hh-actions .hh-profile,.hh-actions .hh-admin,.hh-actions .hh-logout{
  border-radius:8px!important;padding:8px 20px!important;
  font-size:14px!important;font-weight:500!important;
  background:#FF4D6D!important;border-color:#FF4D6D!important;color:#FFFFFF!important;
}
.hh-actions .hh-login:hover,.hh-actions .hh-profile:hover{
  background:#E63E5C!important;border-color:#E63E5C!important;
}

/* --------------------------------------------------------------------------
   ② banner 文字
   规格：components.banner
     title    = { fontSize:48px, fontWeight:700, lineHeight:1.2, color:#2C2D30 }
     subtitle = { fontSize:16px, fontWeight:400, lineHeight:1.6, color:#6B7280 }
   ⚠️ `.banner p` 会**同时命中** `<p class="banner-kicker">`（角标也是 <p>），
      而 (0,1,1) 压过 `.banner-kicker` 的 (0,1,0) —— 必须用 :not() 排除，
      否则角标会被一起放大成 16px。
   -------------------------------------------------------------------------- */
.banner p:not(.banner-kicker){
  font-size:16px!important;line-height:1.6!important;color:#6B7280!important;
}
.banner-kicker{font-size:12px!important}   /* 规格 typography.caption */
.banner h1{line-height:1.2!important}

/* --------------------------------------------------------------------------
   ③ 内容容器 / 课件卡片
   这些类 theme2-v2 从未覆盖过，页内联值一直原样生效。
   规格：layout.contentMaxWidth=1200px、components.courseCard
   -------------------------------------------------------------------------- */
/* ⚠️ 2026-09-21 第九轮：原来是写死的 `max-width:1200px`（设计规格 layout.contentMaxWidth），
   大屏上两侧留白过多。改用 `--hh-content` 流体变量（定义在 sidebar_assets.py 的 :root 里）：
   视口 ≤1552px 时它恒为 1200px，所以**定稿及以下宽度零变化**；更宽才放宽到 2080px。 */
.container{max-width:var(--hh-content)!important}

/* ⚠️ 2026-09-21 第九轮补：联系页的内容列也是写死的 —— `contact.html` 页内
   `.contact-main{max-width:1040px}`，2560 下实测两侧各空 636px（比列表页的 556px 还大）。
   ⚠️ **不能复用 `--hh-content`**：它的下限是 1200px，套到联系页会把 1440 下从 1040 撑到 1200，
   那是改版式。所以用 `--hh-content-narrow`（下限 1040px，上限 1500px）。
   ⚠️ 这条**必须带 `!important`**：页内 <style> 与 `responsive.css` 的
   `@media(min-width:1600px){.contact-main{max-width:1280px}}` 在源码顺序上都能压过外部表，
   实测 2560 下 `.contact-main` 的计算值就是页内那个 1040px —— 那条 1280px 一直是死代码。
   小屏方向安全：`responsive.css` 的 `max-width:1180px → max-width:100%` 与
   `max-width:480px` 只是**上限**，`!important` 让上限变成 1040/1200 也不会溢出
   （块级盒宽度 = min(可用宽, max-width)），实测 375/761/1180 三档 ovf 仍为 0。 */
.contact-main{max-width:var(--hh-content-narrow)!important}

/* 缩略图：页内联是 aspect-ratio:16/9（316px 宽 → 177.75px），规格要求固定 200px */
.cw-cover,.cw-cover-placeholder{aspect-ratio:auto!important;height:200px!important;object-fit:cover}
.cw-body{padding:16px!important}
.cw-card h3{font-size:18px!important;font-weight:600!important;line-height:1.4!important}
.cw-desc{font-size:13px!important;color:#9CA3AF!important;line-height:1.5!important}
/* 会员角标：规格 vip badge = { fontSize:11px, borderRadius:9999px } */
.member-badge{font-size:11px!important;border-radius:9999px!important}

/* --------------------------------------------------------------------------
   ④ 右下角浮层
   规格：components.floatingButtons
     = { position:fixed, right:32px, bottom:80px, gap:12px,
         button:{ borderRadius:9999px, fontSize:14px, fontWeight:600,
                  boxShadow:0 4px 12px rgba(主色,.4) } }
   注意：`.hh-contact-btn` 在 contact-widget.js 里被定成固定 100×42 的胶囊，
   这里只改字号/字重/圆角/阴影，**不动 width/height** —— 那套尺寸是此前
   修过「关注/客服按钮内容格式不一致」bug 的既有约定。
   -------------------------------------------------------------------------- */
.hh-contact{right:32px!important;bottom:80px!important;gap:12px!important}
.hh-contact-actions{gap:12px!important}
.hh-contact-btn{border-radius:9999px!important;font-size:14px!important;font-weight:600!important}
.hh-contact-follow{background:#FF4D6D!important;box-shadow:0 4px 12px #FF4D6D66!important}
.hh-contact-service{background:#FF8C42!important;box-shadow:0 4px 12px #FF8C4266!important}

/* --------------------------------------------------------------------------
   ⑤ 筛选胶囊
   同一个视觉组件在两个页面用了不同类名：index.html = `.grade-chip`、
   coursewares.html = `.chip`。两边都还是旧主题的「大胶囊」（99/999px 圆角、
   7px 15px 内边距、12px 字、字重 700）。
   规格：components.courseGrid.filterTab
     active   = { bgColor:#FF4D6D, textColor:#FFFFFF, borderRadius:8px,
                  padding:6px 16px, fontSize:13px, fontWeight:500 }
     inactive = { bgColor:#F7F8FA, textColor:#6B7280, borderRadius:8px,
                  padding:6px 16px, fontSize:13px, fontWeight:400 }
   ⚠️ 这是本次改动里**最显眼的一处**：胶囊会从「全圆角药丸」变成「8px 圆角矩形」。
   -------------------------------------------------------------------------- */
.chip,.grade-chip{
  border-radius:8px!important;padding:6px 16px!important;
  font-size:13px!important;font-weight:400!important;
  border-color:#E8E9EC!important;background:#F7F8FA!important;color:#6B7280!important;
}
.chip.active,.grade-chip.active{font-weight:500!important}
/* 移动端保留页面原有的紧凑尺寸（规格的 responsive 段未覆盖筛选胶囊） */
@media(max-width:760px){
  .chip,.grade-chip{padding:6px 12px!important;font-size:12px!important}
  .chips,.grade-chips{gap:7px!important}
}

/* --------------------------------------------------------------------------
   ⑥ 右上角登录区定位
   规格：components.topNav.userActions = { position:absolute, top:14px, right:24px }
   ⚠️ `position` 必须保持 absolute（用户此前明确要求「随滚动条移动」，原为 fixed），
      这里只改偏移量。移动端另有一套 fixed 顶栏定位（13px/13px），不动。
   ⚠️ 配套的 `body:has(.hh-actions #authAction:not([hidden])){padding-top:92px}`
      必须保留 —— 它同时是页面顶部留白，也是防止按钮压住内容的那道保险。
   -------------------------------------------------------------------------- */
body>.hh-actions{top:14px!important;right:24px!important}

/* --------------------------------------------------------------------------
   ⑦ 登录弹窗 / 管理台里的「主按钮」
   stage-1 从旧主题继承来的深色主色规则，特异性**高于**本表前面那些规则，
   所以光写 `.submit` 压不住，必须复用同样的高特异性选择器：
     .auth-main .submit          (0,2,0)  ← 压过 `.submit`        (0,1,0)
     .auth-tabs button.active    (0,2,1)  ← 本表此前完全没有覆盖
     .shell .btn.primary         (0,3,0)  ← 压过 `.btn.primary`   (0,2,0)
   实测依据：全站扫描「计算背景仍为墨黑且可交互」的元素，只剩这两个
   （#loginTab / #loginSubmit），修复后归零。
   ⚠️ `background-image:none` 必须写：stage-1 还留下了
      `background-image:initial!important`，会盖掉渐变色。
   -------------------------------------------------------------------------- */
.auth-tabs button.active{
  background:#FF4D6D!important;background-image:none!important;color:#fff!important;
  box-shadow:0 4px 12px #FF4D6D4d!important;
}
.auth-main .submit,.modal .submit,.hh-login-submit,.privacy-footer button{
  background:#FF4D6D!important;background-image:none!important;
  border-color:#FF4D6D!important;color:#fff!important;border-radius:8px!important;
  box-shadow:0 4px 12px #FF4D6D4d!important;
}
.auth-main .submit:hover,.shell .btn.primary:hover{background:#E63E5C!important}
.shell .btn.primary{
  background:#FF4D6D!important;background-image:none!important;
  border-color:#FF4D6D!important;color:#fff!important;
  box-shadow:0 4px 12px #FF4D6D4d!important;
}

/* ==========================================================================
   ⑧ JS 运行时注入的共享样式
   auth-dialog.js / contact-widget.js 会把样式写成
   `<style data-hh-shared-style>` 在运行时插到 head 末尾（在所有 .css 之后）。
   这些颜色**从来没有被 theme2-v2 / theme3 处理过** —— 它们躺在 .js 文件里，
   不在任何 .css 里。旧主题恰好也是墨黑 + 米白，所以一直看不出来；
   换成珊瑚红之后就成了明显的违和点（实测：登录弹窗按钮、隐私弹窗、
   客服卡片的标题/正文/边框都还是原主题色）。
   这些注入规则**不带 !important**，所以这里只要加 !important 就能覆盖，
   不需要拼特异性。
   ========================================================================== */
/* — 登录弹窗（.hh-shared-auth 作用域）— */
.hh-shared-auth .auth-side{
  color:#2C2D30!important;
  background:linear-gradient(150deg,#FFE5EC,#FFF5E6)!important;
}
.hh-shared-auth .auth-side p,.hh-shared-auth .auth-feats li{color:#6B7280!important}
.hh-shared-auth .auth-main{background:#FFFFFF!important}
.hh-shared-auth h2{color:#2C2D30!important}
.hh-shared-auth .auth-sub{color:#6B7280!important}
.hh-shared-auth .auth-top .close{background:#F0F1F3!important;color:#6B7280!important}
.hh-shared-auth .auth-tabs{background:#F0F1F3!important}
.hh-shared-auth .auth-tabs button{color:#6B7280!important}
.hh-shared-auth .auth-tabs button.active{
  background:#FF4D6D!important;color:#fff!important;box-shadow:0 4px 12px #FF4D6D4d!important;
}
.hh-shared-auth .field label{color:#6B7280!important}
.hh-shared-auth .field input{border-color:#E8E9EC!important;background:#FFFFFF!important}
.hh-shared-auth .field input:focus{border-color:#FF4D6D!important}
.hh-shared-auth .password-toggle{color:#6B7280!important}
.hh-shared-auth .submit{
  background:#FF4D6D!important;background-image:none!important;color:#fff!important;
  box-shadow:0 4px 12px #FF4D6D4d!important;
}
.hh-shared-auth .auth-links{color:#9CA3AF!important}
.hh-shared-auth .auth-links a{color:#FF4D6D!important}
.hh-shared-auth .privacy-consent{color:#9CA3AF!important}
.hh-shared-auth .privacy-consent input{accent-color:#FF4D6D!important}
.hh-shared-auth .privacy-link{color:#FF4D6D!important}
.hh-auth-message{color:#E63E5C!important}
/* — 隐私协议弹窗 — */
.hh-auth-privacy-box{background:#FFFFFF!important}
.hh-auth-privacy-head,.hh-auth-privacy-foot{border-color:#E8E9EC!important}
.hh-auth-privacy-head button{background:#F0F1F3!important;color:#6B7280!important}
.hh-auth-privacy-body{color:#6B7280!important}
.hh-auth-privacy-foot button{
  background:#FF4D6D!important;background-image:none!important;color:#fff!important;
  box-shadow:0 4px 12px #FF4D6D4d!important;
}
/* — 客服 / 关注卡片 — */
.hh-contact-card{border-color:#E8E9EC!important;background:#FFFFFF!important}
.hh-contact-card:after{background:#FFFFFF!important;border-color:#E8E9EC!important}
.hh-contact-card h3{color:#2C2D30!important}
.hh-contact-card p{color:#6B7280!important}
.hh-contact-card .hh-qr{border-color:#E8E9EC!important}
.hh-follow-item{background:#F7F8FA!important}
.hh-follow-item span{color:#6B7280!important}
.hh-pay-box{background:#FFFFFF!important}
.hh-pay-box h2{color:#2C2D30!important}
.hh-pay-box p{color:#6B7280!important}
.hh-pay-box img{border-color:#E8E9EC!important}
.hh-pay-close{color:#9CA3AF!important}

/* --------------------------------------------------------------------------
   ⑨ 响应式标题尺寸
   规格：components/../responsive
     tablet = { gridColumns:2, bannerTitleSize:36px }
     mobile = { gridColumns:1, bannerHeight:180px, bannerTitleSize:28px }
   网格列数实测已与规格一致（3 → 2 → 1）。
   ⚠️ **2026-09-20：标题字号不再写死在断点里** —— 见上方 `.banner h1.banner-title`
      的 `clamp(30px, calc((100vw - 228px) × 0.0636 − 6px), 61px)`。
      原因：61px（封顶值）时标题宽 431px，会与右侧 47% 的装饰插画（起点 = 横幅宽 53%）重叠；
      必须随视口连续缩放才不会压到插画。下面这两条 `.banner h1{font-size:...}` 是
      旧断点写法，**特异性 (0,1,1) 低于** `.banner h1.banner-title` (0,2,1)，已被流体字号
      覆盖、等同死代码，删掉以免误导。
        · `@media(max-width:900px) and (min-width:761px){.banner h1{48px}}` —— 实测无效
        · `@media(max-width:760px){.banner h1{28px}}` —— 同上，移动端最终字号由 clamp 的
          min(30px) 或下方移动端专属块（:first-child::after 等）决定。
   -------------------------------------------------------------------------- */
@media(max-width:480px){
  .banner{min-height:180px!important}
}

/* --------------------------------------------------------------------------
   ⑩ 修复一个从 theme2-v2 继承下来的移动端 bug
   页面自身的 ≤600px 规则写的是：
       .banner-kicker,.banner-copy>p,.banner h1{display:none}
   即「移动端 banner 只保留搜索框」。但 theme2-v2（以及本表的 stage-1）
   给 .banner-kicker 写了 `display:inline-flex!important`，把这条压掉了 ——
   结果移动端 banner 里只剩一个**孤零零的角标徽章**（标题/副标题都没了），
   看起来像坏掉。这里让角标跟随页面原意一起隐藏。
   -------------------------------------------------------------------------- */
@media(max-width:600px){
  .banner-kicker{display:none!important}
}

/* --------------------------------------------------------------------------
   ⑪ 主内容区顶栏（设计稿 top-nav）+ 侧栏 logo + banner 装饰
   设计稿把「分类栏（年级标签）+ 搜索 + 登录」全部放进主内容区一条 64px 的顶栏，
   且**紧贴顶部**（headerHeight:64px、borderBottom:none）；banner 里只剩
   标题/副标题/装饰。旧实现把搜索塞在 banner 内部、登录按钮 absolute 浮在右上角，
   两者都对不上，这一节整体改过来。
   顶栏由 site-header.js 动态创建（.hh-topnav），这里只负责样式。
   -------------------------------------------------------------------------- */
/* 顶栏底色 = **半透明白 + 毛玻璃**（2026-09-20 按参考图定稿）。
   参考图量出来的事实：顶栏那一条的颜色 ≈ 它正下方 banner 的颜色 × 50% 白
   （x=300 处 banner #f8e6a4 → 顶栏 #fbf1d5；x=2 处 #fbc0c8 → #fcd2e9；
     分界在 y=53/54，跳变非常干净）。也就是说参考图的顶栏**本身没有底色**，
   就是一层 50% 白玻璃盖在 banner 上。
   所以这里：background-color 用 rgba(255,255,255,.5)，**去掉**原来那条
   「粉→黄」渐变（那是更早一版照另一张稿子做的，和现在这张参考图不符），
   backdrop-filter 兜住 sticky 时从下面滚过的内容 + 把伸进顶栏的装饰糊开。
   ⚠️ 只覆盖 background-color / background-image，不要用 background 简写 ——
      简写会把 `position:sticky` 之类无关属性也一起重置的写法容易踩坑，
      而且这里已经显式写 background-image:none 了。 */
.hh-topnav{
  display:flex!important;align-items:center!important;gap:16px!important;
  height:64px!important;min-height:64px!important;padding:0 24px!important;
  background-color:rgba(255,255,255,.5)!important;
  background-image:none!important;
  backdrop-filter:blur(10px) saturate(1.06)!important;
  border:0!important;
  position:sticky!important;top:0!important;z-index:44!important;
}
/* 顶栏里没有任何「页面自己的内容」时（非首页：年级标签留在页内筛选面板里、
   搜索也在页内），它不该继续当一条 64px 的白色横条 —— 那正是「除首页外上面多了一块」。
   收成一条透明、不吸顶、由内容撑高的行，只留右侧的登录/注册按钮。
   ⚠️ 只在桌面端生效：移动端顶栏要自己带品牌标识（侧栏整条隐藏），
   收掉背景会让移动端头部失去底色。 */
@media(min-width:761px){
  .hh-topnav.hh-topnav-empty{
    height:auto!important;min-height:0!important;
    position:static!important;z-index:auto!important;
    background:transparent!important;background-image:none!important;
    /* ⚠️ 2026-09-20：base 规则新加了 backdrop-filter:blur(10px)，这条空态必须显式关掉。
       否则这 5 个分类页 + 工作台那条「什么都不显示」的空行会带着一层毛玻璃 ——
       在纯白页面底色上看不出差别，但会给浏览器留一个多余的 backdrop root
       （滚动时白算一遍模糊），而且一旦将来空行背后有内容就会糊出来。 */
    backdrop-filter:none!important;-webkit-backdrop-filter:none!important;
    /* 2026-09-20：原来 14px → 这一条 64px 的空行 + 下面 .container 的 24px 一起，
       把「交互网页/图解视频/互动课件/教师工具/课件管理」这 5 页的正文推到 y=88，
       用户反馈「顶部留一片空白」。收到 6px：行高 48px（登录按钮 36px + 上下各 6px），
       正文落到 y≈54。不要再往下压 —— 登录按钮需要这点呼吸位。 */
    padding:6px 24px!important;
  }
  /* 工作台（.shell —— 只有 admin.html 用）跟上面那 5 个分类页不一样：
     它既没有年级标签也没有搜索框，顶栏**永远**是空态；而且它的 .hh-actions
     本来就该隐藏（theme2-v2 里那条 `body:has(.shell)>.hh-actions{display:none}`，
     在 site-header.js 把 .hh-actions 搬进 .hh-topnav-side 之后就不再命中，
     于是这条空行里只剩一枚多余的「登录/注册」按钮）。
     所以这条空行对工作台毫无意义，却把正文整体下推 48px（登录态下 12px）。
     2026-09-20 用户反馈「管理端页面它上面也没有紧贴顶部」→ 整条收掉。
     ⚠️ 特异性 (0,3,1) 压得住 `.hh-topnav` 的 display:flex!important (0,1,0)。 */
  body:has(.shell) .hh-topnav.hh-topnav-empty{display:none!important}
}
.hh-topnav-main{
  flex:1 1 auto!important;min-width:0!important;display:flex!important;align-items:center!important;
  gap:4px!important;overflow-x:auto!important;overflow-y:hidden!important;scrollbar-width:none!important;
}
.hh-topnav-main::-webkit-scrollbar{display:none!important}
.hh-topnav-side{flex:0 0 auto!important;display:flex!important;align-items:center!important;gap:12px!important}
/* 桌面端侧栏已有 logo，顶栏里那份克隆只在移动端露脸。 */
.hh-topnav-brand{display:none!important}
/* 分类栏（年级标签）：选中态是珊瑚色文字 + 600 字重，不是填充胶囊。 */
.hh-topnav .grade-chips{display:flex!important;flex-wrap:nowrap!important;gap:4px!important}
.hh-topnav .grade-chips .grade-chip{
  min-height:36px!important;padding:8px 16px!important;border:0!important;border-radius:8px!important;
  background:transparent!important;color:#6B7280!important;font-size:14px!important;font-weight:500!important;
  white-space:nowrap!important;cursor:pointer!important;
}
.hh-topnav .grade-chips .grade-chip:hover{background:#FFF0F3!important;color:#2C2D30!important}
.hh-topnav .grade-chips .grade-chip.active{background:transparent!important;color:#FF4D6D!important;font-weight:600!important}
/* 搜索框：240×36、#F7F8FA 底、8px 圆角、珊瑚色按钮。 */
.hh-topnav .banner-search{
  width:240px!important;max-width:none!important;height:36px!important;
  display:flex!important;align-items:center!important;gap:8px!important;
  margin:0!important;padding:0 0 0 12px!important;
  background:#F7F8FA!important;border:0!important;border-radius:8px!important;box-shadow:none!important;
}
.hh-topnav .banner-search input{
  flex:1!important;min-width:0!important;padding:0!important;border:0!important;background:transparent!important;
  color:#2C2D30!important;font-size:13px!important;outline:none!important;
  /* ⚠️ 2026-09-21 第二轮：**必须给高度**。父级是 `align-items:center`，`<input>` 不是
     被拉伸的替换元素 —— 不给高度时它的盒子高度 = 13px 文字的行盒 ≈ **18px**。
     实测 1440/1024/761 三档都只有 138×18，也就是说这个搜索框真正的可点区域只有
     18px 高（父级那 36px 白底里上下各 9px 点下去不聚焦）。这是「用户可交互的区域」
     里最典型的一个命中区缺陷。给 100%（= 父级 36px）后整块白底都能点。 */
  height:100%!important;
}
.hh-topnav .banner-search input::placeholder{color:#C4C9D1!important}
.hh-topnav .banner-search button{
  flex:0 0 auto!important;height:36px!important;padding:0 16px!important;border:0!important;border-radius:8px!important;
  background:#FF4D6D!important;color:#FFFFFF!important;font-size:13px!important;font-weight:500!important;cursor:pointer!important;
  /* ⚠️ 同上：theme3 第 36 行那条 `.banner-search button{min-height:44px}` 是给
     **banner 内部**的大搜索框用的（那儿确实是 44px）。顶栏里搜索框只有 36px 高，
     44px 的按钮会上下各溢出 4px —— 实测按钮 44px 嵌在 36px 的框里。
     `min-height` 的优先级高于 `height`，所以必须显式压掉，不能只写 height。 */
  min-height:36px!important;
}
.hh-topnav .banner-search button:hover{background:#E63E5C!important}
/* ⚠️ 2026-09-21（同日第七轮）：顶栏搜索框**没有可见的键盘焦点指示**。
   `.hh-topnav .banner-search input` 上有 `outline:none!important`（去掉 UA 默认轮廓本身是对的
   —— 那个方框和这个 8px 圆角灰底不搭），但**没有补任何替代品**。
   全站键盘焦点审计（13 页 / 369 个可交互元素，逐个 focus 比较计算样式）里，
   它是**唯一**一个「`:focus-visible` 已匹配、样式零变化」的元素 —— 即 WCAG 2.4.7 焦点可见性失败。
   修法：把焦点环画在**容器**上，而不是给 input 自己加 outline。
   input 只占 36px 高胶囊里的一部分，方框套在它上面很难看；套在整颗胶囊上才是主流搜索框的观感。
   ⚠️ 用 `:has(input:focus-visible)` 而不是 `:focus-within`：后者在 Tab 到「搜索」按钮时
   也会点亮容器，叠上按钮自己的轮廓就变成两道环。
   `:has()` 在本项目已是硬依赖（顶栏布局全靠 `body:has(.hh-topnav:not(...))`），不引入新风险。
   双环写法（白内圈 + 深外圈）保证在任何底色上都看得见。
   ⚠️⚠️ **必须写两条** —— 容器上的 `box-shadow` 被两条不同特异性的规则改过：
     · 基础 `.hh-topnav .banner-search` 的 **(0,2,0)** → `box-shadow:none`
     · 正常态 `body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav .banner-search`
       的 **(0,5,1)** → `box-shadow:0 2px 8px #00000014`（同时给它白色底 + 搜索图标背景）
   只写 (0,3,1) 那条会被 (0,5,1) 压掉 —— **实测焦点环完全不出现**（计算值仍是那条 8% 阴影）。
   所以第二条必须复用同一个 `body:has(...)` 前缀再加 `:has(...)`，得 **(0,6,1)** 才压得住。
   教训：**加 `!important` 之前先确认「谁在改同一个属性、它的特异性是多少」**，
   本文件里 `body:has(.hh-topnav:not(.hh-topnav-empty))` 这条前缀把很多顶栏规则抬到了 (0,5,x)。
   ⚠️⚠️⚠️ **逗号分隔的多重阴影里 `!important` 只能写一次、且必须在整条声明末尾。**
   写成 `box-shadow:A!important,B!important` 会让**整条声明被 CSS 解析器丢弃**
   （`!important` 后紧跟 `,` 非法）—— 症状是「规则在文件里、选择器也确实命中、但计算值纹丝不动」。
   实测排查手法：遍历 `document.styleSheets` 找命中该元素且声明了同名属性的规则，
   被丢弃的声明在 CSSOM 里 `r.style.boxShadow === ''`，**根本不会出现在列表里** ——
   这就是「规则明明写了却不生效」的确定性判据。正确写法：
   `box-shadow:0 0 0 2px #FFFFFF,0 0 0 4px #2C2D30!important;` */
.hh-topnav .banner-search:has(input:focus-visible){
  box-shadow:0 0 0 2px #FFFFFF,0 0 0 4px #2C2D30!important;
}
body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav .banner-search:has(input:focus-visible){
  box-shadow:0 0 0 2px #FFFFFF,0 0 0 4px #2C2D30!important;
}
/* 操作区跟着进顶栏：撤掉它原来「absolute 浮在右上角 + 独占 118px 宽列」的定位。
   选择器特异性 (0,2,0) 压得住 body>.hh-actions (0,1,1) 和移动端那条 position:fixed。
   ⚠️ `flex:0 0 auto` 是**必须**的：账号按钮是登录入口，绝不能是「被压掉的那个」。
      顶栏空间不够时该让位的是搜索框与年级标签（它们各有别的入口/本身就是滚动区），
      而登录按钮一旦被压缩，按钮文字就会被裁。 */
.hh-topnav .hh-actions{
  position:static!important;top:auto!important;right:auto!important;
  width:auto!important;min-width:0!important;flex:0 0 auto!important;
  display:flex!important;flex-direction:row!important;align-items:center!important;gap:8px!important;
}
.hh-topnav .hh-actions .hh-login{min-height:36px!important;padding:8px 20px!important;font-size:14px!important;font-weight:500!important}
/* 登录按钮已经回到文档流里，原来那 92px 的顶部占位（防遮挡 coursewares 的排序 tabs）不再需要。 */
body:has(.hh-topbar){padding-top:0!important}
/* 顶栏自己占掉 64px，容器原来的 56px 顶部留白要收掉，否则 banner 被推得太低。
   2026-09-20：24px → 6px。这条只对「顶栏为空」的页面生效（首页/个人中心那条
   `body:has(.hh-topnav:not(.hh-topnav-empty)) .container{padding:0 0 56px}`
   特异性更高，已经把它们的 padding-top 归零了）。 */
body:has(.hh-topnav) .container{padding-top:6px!important}

/* 侧栏 logo：保留设计稿的「三色几何图形」，但尺寸回到 theme2-v2（34px logo / 15px 标题），
   否则放大的侧栏元素会把历史记录挤掉。 */
.hh-brand{
  width:100%!important;padding:2px 8px 22px!important;
  gap:10px!important;align-items:center!important;
}
.hh-logo,.logo{
  display:grid!important;place-items:center!important;
  width:34px!important;height:34px!important;flex:0 0 34px!important;
  padding:0!important;background:transparent!important;border:0!important;box-shadow:none!important;
  font-size:0!important;letter-spacing:0!important;
}
.hh-logo svg,.logo svg{display:block!important;width:34px!important;height:34px!important}
.hh-brand strong{color:#2C2D30!important;font-size:15px!important;font-weight:700!important;letter-spacing:0!important;white-space:nowrap!important}
/* 收起侧栏时文字整体退场（新版 logo 用 <strong> 承载文案，不再是 <span>，
   而 theme2-v2 的收起规则只认 `.hh-brand>span:not(.hh-logo)`）。 */
.hh-sidebar-collapsed .hh-brand>strong{display:none!important}

/* banner：设计稿「让知识看得见，让课堂动起来」。搜索框已经搬去顶栏，
   这里只剩标题/副标题/装饰，内容垂直居中（设计稿 left:48px / top:50%）。
   ⚠️ 标题用 <span> 分行而不是 <br>：每行要各挂一条青色笔刷底纹（见下）。 */
.banner{
  display:flex!important;align-items:center!important;
  min-height:240px!important;margin:0 0 24px!important;padding:0 48px!important;
  border-radius:20px!important;overflow:hidden!important;
}
/* z-index 提到 2：星星层是 0、装饰 SVG 是 1，文案必须压在最上面，否则星星会飘到字上。 */
.banner-copy{max-width:560px!important;margin:0!important;z-index:2!important}
/* 标题：2026-09-20 由 46px 放大到 61px（上限）。
   依据：参考图（887×226）里标题第 1 行墨迹 x169..489 = 321px 宽，占横幅宽度 321/887 = 36.2%；
   本站横幅内容宽 1212px，要拿到同样的 36.2% 需要 439px。
   46px 时实测第 1 行 326px（占 26.9%）→ 需放大到 46 × 439/326 = 62px，取 61px。
   ⚠️ 注意这是「按宽度比例」而不是「按绝对字号」对齐：参考图标题绝对字号 ≈50px（每字 advance
      50.2px），本站 61px 比它大 1.22 倍。之所以不按绝对值，是因为本站横幅高 343px、参考图只有
      172px，照绝对值搬过来标题会淹没在横幅里。用户已确认取「比例一致」这条路。
   行高 1.2 → 1.12：参考图两行墨迹顶 y65 / y121，行距 56px ÷ 字号 50px = 1.12。
   墨色 #26181A 保留（参考图墨芯均值 #20120e，几乎一致）。

   ⚠️⚠️ **字号必须是流体的，不能只写死 61px**（2026-09-20 踩过）
   61px 时标题宽 431px（实测 7.07 × 字号）。横幅右侧 47% 是装饰插画（起点 = 横幅宽的 53%），
   所以要求 `48(左内边距) + 7.07 × 字号 ≤ 0.50 × 横幅宽`。
   桌面端横幅宽 = 视口宽 − 248(侧栏，2026-09-21 由 228 加宽)，代入可得：
      视口 1440 → 上限 77.5px（61 够用）
      视口 1132 → 上限 55.7px  ← 61px 从这里开始压到插画上
      视口 1000 → 上限 46.4px
      视口  761 → 上限 29.5px
   写死 61px 时实测 1000px 视口下标题直接盖住粉色方块和甜甜圈（46px 时刚好不碰，是本次放大引入的回归）。
   所以改成 `clamp(30px, calc((100vw − 248px) × 0.0636 − 6px), 61px)`：
   在 1440 封顶 61px、1132→50.2px、1000→41.8px、900→35.5px、761→30px（触底），全程留 ~8% 余量。
   `calc()` 里带减号的项放在最后，且 `100vw` 与 `248px` 之间必须有空格，否则被当成除法/非法。
   ⚠️ 侧栏宽度改了必须同步改这里的基准 —— 它假设「内容列 = 视口 − 侧栏」，写错会让标题压到插画。
   2026-09-21 第二轮：侧栏改成流体 `--hh-sbw`（196→248，见 scripts/sidebar_assets.py），
   所以这里连同 `.banner-sub` / `.banner` 的 padding 一起改成 `var(--hh-sbw)`。
   写死 248 会让 761–1280px 区间的横幅按「比实际更窄的内容列」缩放 —— 文案偏小、
   插画却按实际宽度铺开，两者错位。 */
.banner h1.banner-title{
  color:#1c0f0d!important;
  /* ⚠️⚠️ **2026-09-21（阶段 L）：上限 61px → 72px，斜率按参考稿重算。**
     参考稿（样例.txt，画布 1352×345）实测标题第 1 行墨迹 x257..715（宽 458）、
     墨迹高 77、两行墨迹顶间距 85 → 字号 ≈ 85 ÷ 1.12 ≈ 76px。
     本站同宽度下 61px 只量到宽 384 / 高 61，明显偏小 —— 这就是用户说的
     「大方块差距还是很大」里除插画之外的第二个原因。
     取 72px（宽 454、高 72），比 76 略保守，留出字体差异的余量。
     斜率 = 72 ÷ 1352 = 0.0533（横幅宽 = 100vw − 248 侧栏）。
     同时保留原来的防压插画约束：左内边距 19% + 标题宽 6.30×字号 必须 ≤ 插画左缘。
     插画左缘 = 横幅宽 − 609px（609 = 343 × 1197/674，按高度定尺），
     在 1352 宽下 = 743px；19%×1352 + 6.30×72 = 257 + 454 = 711 < 743 ✓（余 32px）。
     若将来再放大字号，**必须重算这条**，否则标题会压到黄方块上。 */
  font-size:clamp(30px,calc((100vw - var(--hh-sbw)) * 0.05473),74px)!important;
  font-weight:900!important;
  line-height:1.12!important;letter-spacing:.5px!important;margin:0 0 18px!important;
  /* 2026-09-20（高清参考图 2639×893 复测）：让 h1 收缩到标题文字宽度，
     这样 `.banner-title::after` 的 left:% 才能相对「标题文字」而非整块 banner-copy。 */
  display:inline-block!important;width:max-content!important;position:relative!important;
  text-shadow:
    3px 0 0 #fff,-3px 0 0 #fff,0 3px 0 #fff,0 -3px 0 #fff,
    2px 2px 0 #fff,-2px 2px 0 #fff,2px -2px 0 #fff,-2px -2px 0 #fff,
    0 0 12px #ffffffcc;
}
.banner-title>span{display:block!important;position:relative!important;width:max-content!important}
/* 薄荷青笔刷底纹 —— 2026-09-20 改为「按参考图逐段复刻」。
   参考图量出的是**多段短笔刷**，不是一条长刷子：
     · 第 1 行（墨迹 x169..489，宽 321）：一段 x267..316（30.5%~45.8%）、一段 x397..475（71.0%~95.3%）
     · 第 2 行（墨迹 x174..474，宽 301）：一段 x278..321（34.6%~48.8%）
   实现：`::before` 铺满整行，用**多段 linear-gradient 色标**把非笔刷区间做成 transparent，
   于是一个伪元素就能出两段刷子（第 1 行）或一段（第 2 行）。
   放在文字层之下（z-index:-1），白色描边会把压住字身的部分盖掉，露出来的正好是笔锋。
   ⚠️ 色标百分比是相对**该行 span 的宽度**（span 是 width:max-content，宽度≈该行墨迹宽），
      不是相对 .banner —— 所以比例可以直接照抄参考图量出的占比。
   ⚠️ 这里原来多了一个注释结束符（星号 + 斜杠）把注释提前闭合，剩下两行成了
      「非法选择器 + 声明块」，CSS 错误恢复会连同紧随其后的 `.banner-title>span::before{...}`
      一起丢弃，于是笔刷整条消失（实测 content 为 none、position 为 static）。
   ⚠️ 写这条说明时我自己又把同样的错误犯了一遍 —— 在注释正文里打出了那两个字。
      **CSS 注释体内永远不能出现注释结束符**，要提它就描述成「星号加斜杠」。 */
.banner-title>span::before{
  content:""!important;position:absolute!important;z-index:-1!important;
  left:0!important;width:100%!important;height:13px!important;bottom:-2px!important;
  /* 默认 = 第 2 行的单段笔刷。高清参考图（2639×893）量出第 2 行墨迹 x830..961、
     行宽 x527..1330 → 37.7%~54.0%。芯色 #57d5be，略深 #50c4af。 */
  background:linear-gradient(90deg,
    #00000000 0 37.7%,
    #6FD9C4 37.7%,#50C4AF 44%,#50C4AF 48%,#7EDDCB 54%,
    #00000000 54%)!important;
  border-radius:4px!important;
}
/* 第 1 行：两段笔刷（高清参考图量出 x801..951 → 30.0%~46.4%、x1178..1309 → 71.3%~85.6%）。 */
.banner-title>span:first-child::before{
  background:linear-gradient(90deg,
    #00000000 0 30%,
    #8CE7D2 30%,#57D5BE 36%,#57D5BE 42%,#7EDDCB 46.4%,
    #00000000 46.4% 71.3%,
    #8CE7D2 71.3%,#57D5BE 78%,#57D5BE 84%,#8CE7D2 85.6%,
    #00000000 85.6%)!important;
}
/* 首字左上角那枚珊瑚色小标记 —— 高清参考图量出**两枚叠放**的小标记
   （x509..558 y307..367，上段 32×32 更饱和 #ee6d62、下段 42×23 稍亮 #e5766b/#ec6b60）。
   相对第 1 行 span（让 墨迹顶 y320、左 x527）：珊瑚左缘 -18px、顶 -13px，缩放后约
   left:-8px top:-6px，尺寸约 22×26。用 180° 渐变在一段伪元素里出两段，旋转 -18°。 */
.banner-title>span:first-child::after{
  content:""!important;position:absolute!important;z-index:-1!important;
  left:-8px!important;top:-6px!important;width:22px!important;height:26px!important;
  border-radius:4px!important;transform:rotate(-18deg)!important;
  background:linear-gradient(180deg,
    #EE6D62 0%,#EE6D62 44%,
    #00000000 44%,#00000000 52%,
    #E5766B 52%,#EC6B60 100%)!important;
}
/* 第 3 枚珊瑚：高清参考图「看/得」之间一枚倾斜小标记（x906..943 y383..429，38×47，
   芯色 #c98161）。相对 h1（第 1 行左 x527、宽 ~913 → 43.6%）+ 行内 71.7% 高度处，旋转约 -40°。 */
.banner-title::after{
  content:""!important;position:absolute!important;z-index:-1!important;
  left:43.6%!important;top:30px!important;width:17px!important;height:21px!important;
  background:#C98161!important;border-radius:4px!important;transform:rotate(-40deg)!important;
}
/* 副标题：2026-09-21（阶段 L）按参考稿重算 —— 17px → 22px。
   参考稿实测：副标题墨迹 x261..630（宽 369，17 个字 → 21.7px/字），
   墨迹高 22；标题墨迹宽 458。两行墨迹顶间距 26px。
   本站 17px 时墨迹宽 288 / 高 15，明显偏小。
   取 22px（= 21.7 ÷ 0.99，0.99 是本站 17px 时量出的「px/字 ÷ 字号」系数）。
   斜率 = 22 ÷ 1352 = 0.01627，与标题同一套 `100vw − 248px` 基准，保证整块文案等比缩放。
   行高 1.5 保留：参考稿行距 26px ÷ 22px = 1.18 偏紧，但 1.18 在中文里会显得挤，
   且本站在 22px 下 1.5 的行距与参考稿「两行间距 4px」的观感接近，故不动。 */
.banner-copy>p.banner-sub{
  /* 高清参考图副标题逐行扫描均值 #5f5333（rgb 95,83,51，比原 #6E5F3C 更暗更橄榄）。 */
  color:#5b4d2c!important;
  font-size:clamp(13px,calc((100vw - var(--hh-sbw)) * 0.01627),22px)!important;
  /* 行高 1.5 → 1.2：参考稿两行副标题墨迹顶间距 26px ÷ 字号 22px = 1.18。
     1.5 会撑到 33px，比参考稿松 27%，整块文案被拉长、标题相对上移。 */
  line-height:1.2!important;
  margin:0!important;font-weight:500!important;
}
/* 设计稿 decorations：黄方块（带笑脸）+ 顶上小粉方块 / 粉圆锥 / 粉甜甜圈+黄圆 /
   青环 / 珍珠球 / 爱心气泡 / 金币 / 四角星。

   ⚠️⚠️ **2026-09-21（阶段 L）：从「手绘 SVG 近似」换成参考稿原图位图。**
   用户反馈「大方块跟我提供的差距还是很大」—— 手绘 SVG 再调也追不上 3D 渲染稿：
   方块体积感、甜甜圈的厚度与高光、青环的透视、金币/珍珠球全都对不上。
   所以直接**从参考稿里把插画抠出来**当背景图：
     · 源：`/Users/qlf/Documents/样例.txt` 内嵌 PNG（2639×893，即 1352×345 画布的 1.91×）
     · 裁切：画布 x 726..1352 / y 0..345  →  源图 x 1413..2610 / y 100..774（1197×674）
     · 左侧 30% 做了 smoothstep alpha 羽化，与 CSS 渐变无缝拼接
     · 编码：无头 Chrome `canvas.toDataURL('image/webp',0.92)` → 66 KB
       （系统 `sips` **不能写** WebP，只能读；Python 标准库也不行 → 用 Chrome 转）
     · 落地：`frontend/img/banner-illust.webp`（仓库里唯一一张位图资源）
   裁切脚本见 `_verify/banner-illust/make-illust.py`。

   定位：`auto 100%` + `right bottom` → 高 = banner 高（343px），宽 = 343×1197/674 ≈ 609px，
   左缘落在 743px（参考稿是 726px，差 17px ≈ 1.3%，肉眼不可辨）。
   ⚠️ 不要改成 `100% auto`：那会按宽度缩放，视口越宽方块越大；按高度缩放才与 banner 等高。

   历史（留着提醒）：这里原本是内联 SVG（viewBox 0 0 887 226），按 1.622 倍映射到
   343px 高的 banner 上。换成位图后，`887/226` 那套换算与 `%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20887%20226%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22yT%22%20x1%3D%22.15%22%20y1%3D%220%22%20x2%3D%22.85%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FCF6D4%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23F0D996%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22yL%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23F8DE6C%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23EDC643%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22yR%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23F6AC3A%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E88A1D%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22pT%22%20x1%3D%22.15%22%20y1%3D%220%22%20x2%3D%22.85%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FEF9FC%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23F5DCE9%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22pL%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FCCEDB%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23EEA0B8%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22pR%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23F1849C%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E0527A%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22dn%22%20x1%3D%22.15%22%20y1%3D%220%22%20x2%3D%22.9%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23F8B2C1%22%2F%3E%3Cstop%20offset%3D%22.45%22%20stop-color%3D%22%23F0869C%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E15578%22%2F%3E%3C%2FlinearGradient%3E%3CradialGradient%20id%3D%22yb%22%20cx%3D%22.33%22%20cy%3D%22.26%22%20r%3D%22.9%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FEF4B8%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23F7DA5F%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E8B721%22%2F%3E%3C%2FradialGradient%3E%3ClinearGradient%20id%3D%22cn%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23EE8AAC%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23F8A8C4%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23FCC8DA%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22hb%22%20x1%3D%22.1%22%20y1%3D%220%22%20x2%3D%22.9%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23F79AB0%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E04E6E%22%2F%3E%3C%2FlinearGradient%3E%3CradialGradient%20id%3D%22gd%22%20cx%3D%22.36%22%20cy%3D%22.28%22%20r%3D%22.92%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FEF7CB%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23F7DC72%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E9B62A%22%2F%3E%3C%2FradialGradient%3E%3ClinearGradient%20id%3D%22rbA%22%20x1%3D%220%22%20y1%3D%221%22%20x2%3D%221%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23BFF0F6%22%2F%3E%3Cstop%20offset%3D%22.32%22%20stop-color%3D%22%23FFFFFF%22%2F%3E%3Cstop%20offset%3D%22.66%22%20stop-color%3D%22%23FFD8E4%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23FFB7C8%22%2F%3E%3C%2FlinearGradient%3E%3CradialGradient%20id%3D%22pe%22%20cx%3D%22.34%22%20cy%3D%22.28%22%20r%3D%22.9%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FFFFFF%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%23F2F6F8%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23D3DEE4%22%2F%3E%3C%2FradialGradient%3E%3CradialGradient%20id%3D%22lv%22%20cx%3D%22.34%22%20cy%3D%22.28%22%20r%3D%22.9%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23EDF1FB%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23B8C5EA%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%238FA4D8%22%2F%3E%3C%2FradialGradient%3E%3CradialGradient%20id%3D%22pc%22%20cx%3D%22.34%22%20cy%3D%22.28%22%20r%3D%22.9%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23FFE3CB%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23FFB89A%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23E68868%22%2F%3E%3C%2FradialGradient%3E%3Cfilter%20id%3D%22sh%22%20x%3D%22-60%25%22%20y%3D%22-60%25%22%20width%3D%22220%25%22%20height%3D%22260%25%22%3E%3CfeDropShadow%20dx%3D%220%22%20dy%3D%227%22%20stdDeviation%3D%227%22%20flood-color%3D%22%238A5A00%22%20flood-opacity%3D%22.2%22%2F%3E%3C%2Ffilter%3E%3Cfilter%20id%3D%22shp%22%20x%3D%22-60%25%22%20y%3D%22-60%25%22%20width%3D%22220%25%22%20height%3D%22260%25%22%3E%3CfeDropShadow%20dx%3D%220%22%20dy%3D%225%22%20stdDeviation%3D%225%22%20flood-color%3D%22%238A3350%22%20flood-opacity%3D%22.2%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Cg%20transform%3D%22translate%28594%2050%29%20rotate%28-8%29%22%3E%3Cpath%20d%3D%22M0%20-16%20L3.84%20-3.84%20L16%200%20L3.84%203.84%20L0%2016%20L-3.84%203.84%20L-16%200%20L-3.84%20-3.84%20Z%22%20fill%3D%22%23FBD34E%22%20opacity%3D%220.95%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate%28795%2048%29%20rotate%2810%29%22%3E%3Cpath%20d%3D%22M0%20-19%20L4.56%20-4.56%20L19%200%20L4.56%204.56%20L0%2019%20L-4.56%204.56%20L-19%200%20L-4.56%20-4.56%20Z%22%20fill%3D%22%23FFFFFF%22%20opacity%3D%220.92%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate%28866%2044%29%20rotate%280%29%22%3E%3Cpath%20d%3D%22M0%20-9%20L2.16%20-2.16%20L9%200%20L2.16%202.16%20L0%209%20L-2.16%202.16%20L-9%200%20L-2.16%20-2.16%20Z%22%20fill%3D%22%23FFFFFF%22%20opacity%3D%220.85%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22translate%28632%20219%29%20rotate%2814%29%22%3E%3Cpath%20d%3D%22M0%20-15%20L3.6%20-3.6%20L15%200%20L3.6%203.6%20L0%2015%20L-3.6%203.6%20L-15%200%20L-3.6%20-3.6%20Z%22%20fill%3D%22%235FDCEE%22%20opacity%3D%220.9%22%2F%3E%3C%2Fg%3E%3Cg%20opacity%3D%22.5%22%3E%3Cellipse%20cx%3D%22717%22%20cy%3D%22162%22%20rx%3D%22162%22%20ry%3D%2256%22%20fill%3D%22none%22%20stroke%3D%22url%28%23rbA%29%22%20stroke-width%3D%2226%22%20transform%3D%22rotate%28-17%20717%20162%29%22%2F%3E%3C%2Fg%3E%3Cg%20opacity%3D%22.42%22%3E%3Cellipse%20cx%3D%22717%22%20cy%3D%22162%22%20rx%3D%22162%22%20ry%3D%2256%22%20fill%3D%22none%22%20stroke%3D%22%23FFDF86%22%20stroke-width%3D%2215%22%20stroke-dasharray%3D%22200%20520%22%20stroke-dashoffset%3D%22-150%22%20transform%3D%22rotate%28-17%20717%20162%29%22%2F%3E%3C%2Fg%3E%3Cg%20opacity%3D%22.45%22%3E%3Cellipse%20cx%3D%22726%22%20cy%3D%22215%22%20rx%3D%2230%22%20ry%3D%2213%22%20fill%3D%22%23E86FB0%22%20transform%3D%22rotate%28-14%20726%20215%29%22%2F%3E%3C%2Fg%3E%3Cg%20filter%3D%22url%28%23shp%29%22%3E%3Cellipse%20cx%3D%22610%22%20cy%3D%2296%22%20rx%3D%2233%22%20ry%3D%2210%22%20fill%3D%22%23E4708F%22%2F%3E%3Cpath%20d%3D%22M615%2057%20L643%2096%20L610%20106%20Z%22%20fill%3D%22url%28%23cn%29%22%2F%3E%3Cpath%20d%3D%22M615%2057%20L577%2096%20L610%20106%20Z%22%20fill%3D%22%23EE86A8%22%2F%3E%3Cpath%20d%3D%22M615%2057%20L577%2096%20L610%20106%20Z%22%20fill%3D%22%23FFFFFF%22%20opacity%3D%22.16%22%2F%3E%3C%2Fg%3E%3Cg%3E%3Cellipse%20cx%3D%22736%22%20cy%3D%2278%22%20rx%3D%2233%22%20ry%3D%2222%22%20fill%3D%22none%22%20stroke%3D%22url%28%23dn%29%22%20stroke-width%3D%2226%22%2F%3E%3Cellipse%20cx%3D%22729%22%20cy%3D%2268%22%20rx%3D%2227%22%20ry%3D%2222%22%20fill%3D%22url%28%23yb%29%22%2F%3E%3Cellipse%20cx%3D%22719%22%20cy%3D%2258%22%20rx%3D%229%22%20ry%3D%226.5%22%20fill%3D%22%23FFFADF%22%20opacity%3D%22.8%22%2F%3E%3C%2Fg%3E%3Ccircle%20cx%3D%22790%22%20cy%3D%22104%22%20r%3D%229%22%20fill%3D%22url%28%23pe%29%22%2F%3E%3Ccircle%20cx%3D%22565%22%20cy%3D%22152%22%20r%3D%2210.5%22%20fill%3D%22url%28%23pc%29%22%2F%3E%3Ccircle%20cx%3D%22562%22%20cy%3D%22149%22%20r%3D%223.2%22%20fill%3D%22%23FFFFFF%22%20opacity%3D%22.75%22%2F%3E%3Cg%20opacity%3D%22.5%22%3E%3Cpath%20d%3D%22M540%20198%20C590%20130%20680%20115%20760%20100%20S855%2075%20880%2050%22%20fill%3D%22none%22%20stroke%3D%22url%28%23rbA%29%22%20stroke-width%3D%2226%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fg%3E%3Cg%20opacity%3D%22.35%22%3E%3Cpath%20d%3D%22M548%20192%20C596%20132%20682%20118%20760%20103%20S850%2078%20874%2054%22%20fill%3D%22none%22%20stroke%3D%22%23FFFFFF%22%20stroke-width%3D%2210%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fg%3E%3Cg%20filter%3D%22url%28%23sh%29%22%3E%3Cpath%20d%3D%22M676%20106%20L750%20143%20L676%20176%20L602%20140%20Z%22%20fill%3D%22url%28%23yT%29%22%2F%3E%3Cpath%20d%3D%22M602%20140%20L676%20176%20L676%20217%20L599%20200%20Z%22%20fill%3D%22url%28%23yL%29%22%2F%3E%3Cpath%20d%3D%22M676%20176%20L750%20143%20L750%20205%20L676%20217%20Z%22%20fill%3D%22url%28%23yR%29%22%2F%3E%3Cpath%20d%3D%22M602%20140%20L676%20176%20L750%20143%22%20fill%3D%22none%22%20stroke%3D%22%23FFF8DC%22%20stroke-width%3D%222%22%20opacity%3D%22.45%22%2F%3E%3C%2Fg%3E%3Cpath%20d%3D%22M614%20184%20C624%20203%20641%20207%20659%20200%22%20fill%3D%22none%22%20stroke%3D%22%23FFFFFF%22%20stroke-width%3D%225%22%20stroke-linecap%3D%22round%22%20opacity%3D%22.9%22%2F%3E%3Cg%20filter%3D%22url%28%23shp%29%22%3E%3Cpath%20d%3D%22M679%2094%20L715%20111%20L679%20128%20L643%20111%20Z%22%20fill%3D%22url%28%23pT%29%22%2F%3E%3Cpath%20d%3D%22M643%20111%20L679%20128%20L679%20156%20L643%20146%20Z%22%20fill%3D%22url%28%23pL%29%22%2F%3E%3Cpath%20d%3D%22M679%20128%20L715%20111%20L715%20146%20L679%20156%20Z%22%20fill%3D%22url%28%23pR%29%22%2F%3E%3Cpath%20d%3D%22M643%20111%20L679%20128%20L715%20111%22%20fill%3D%22none%22%20stroke%3D%22%23FFF4F8%22%20stroke-width%3D%221.6%22%20opacity%3D%22.55%22%2F%3E%3C%2Fg%3E%3Cg%20transform%3D%22rotate%28-7%20792%20153%29%22%20filter%3D%22url%28%23shp%29%22%3E%3Crect%20x%3D%22770%22%20y%3D%22128%22%20width%3D%2245%22%20height%3D%2244%22%20rx%3D%2214%22%20fill%3D%22url%28%23hb%29%22%2F%3E%3Cpath%20d%3D%22M779%20166%20L774%20184%20L795%20168%20Z%22%20fill%3D%22url%28%23hb%29%22%2F%3E%3Cpath%20d%3D%22M792%20167%20C786%20160%20780%20156%20780%20150%20C780%20145%20784%20142%20788%20143%20C790%20144%20791%20145%20792%20147%20C793%20145%20794%20144%20796%20143%20C800%20142%20804%20145%20804%20150%20C804%20156%20798%20160%20792%20167%20Z%22%20fill%3D%22%23FFFFFF%22%2F%3E%3C%2Fg%3E%3Cg%3E%3Cellipse%20cx%3D%22811%22%20cy%3D%22219%22%20rx%3D%2230%22%20ry%3D%2223%22%20fill%3D%22%23F5E6AC%22%2F%3E%3Cellipse%20cx%3D%22811%22%20cy%3D%22219%22%20rx%3D%2224%22%20ry%3D%2218%22%20fill%3D%22url%28%23gd%29%22%2F%3E%3Cellipse%20cx%3D%22811%22%20cy%3D%22219%22%20rx%3D%2221%22%20ry%3D%2215.5%22%20fill%3D%22none%22%20stroke%3D%22%23EDC24A%22%20stroke-width%3D%222.2%22%2F%3E%3Cpath%20d%3D%22M804%20217%20q7%20-7%2013%20-1%20q-6%206%20-13%201%22%20fill%3D%22none%22%20stroke%3D%22%23E0A312%22%20stroke-width%3D%222.4%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fg%3E%3Ccircle%20cx%3D%22881%22%20cy%3D%22189%22%20r%3D%228%22%20fill%3D%22url%28%23lv%29%22%2F%3E%3Ccircle%20cx%3D%22878%22%20cy%3D%22186%22%20r%3D%222.6%22%20fill%3D%22%23FFFFFF%22%20opacity%3D%22.7%22%2F%3E%3C%2Fsvg%3E` 占位符
   都不再需要 —— 但 `BANNER_DECOR` 常量仍保留（底部 mobile 规则还在用它的比例说明）。 */
.banner:after{
  /* ⚠️ z-index 必须是 0（不能是 1）：`.banner-copy`（文案块）在 theme2-v2 里也是
     `z-index:1`，同值时**后生成者胜** —— 而 `::after` 排在所有子节点之后，
     会反过来盖住标题。位图插画比以前的手绘 SVG 宽得多（609px），
     窄屏下左缘会伸进文案区，必须靠层级而不是靠「正好不重叠」来保证可读性。 */
  z-index:0!important;
  width:auto!important;height:auto!important;inset:0!important;
  border:0!important;filter:none!important;
  background:url("img/banner-illust.webp") no-repeat right bottom/auto 100%!important;
}

/* ⚠️ 参考稿的星星**已经烘进上面那张位图**了（四角星、细闪点都在里面）。
   再把 `.banner-stars` 的 7 颗动画星星叠上去就是双份，画面比参考稿更乱。
   桌面端（用位图的那档）整组收掉；移动端位图被缩到 112px 且 opacity .55，
   星星反而是有用的点缀，保留。 */
@media(min-width:761px){
  .banner-stars{display:none!important}
}

/* ---------------------------------------------------------------------------
   星星分散特效（`.banner-stars > .bstar-1..7`）
   index.html 里这 7 个 <span> 一直存在，但**从来没有任何样式** —— 它们既没有尺寸
   也没有背景，等于不可见。这里补全：
     · 形状：四角星用 clip-path 画（比 data-URI 背景更省、还能直接吃渐变色）；
     · 分散：每颗星带不同的 delay 依次「弹」出来（scale 0→1 + 回弹缓动），
             视觉上就是从中心四散开的一瞬间；
     · 常驻：之后接一段缓慢的明暗闪烁，让画面不呆。
   ⚠️ 入场用 `scale`（独立属性）、闪烁用 `opacity` —— 两条动画**不能同时改同一个属性**，
      否则后声明的那条会全程压住前一条，入场动画直接失效（实测过）。
   ⚠️⚠️ **`opacity` / `scale` 这两条声明绝对不能加 `!important`。**
      层叠顺序里「important author 声明」排在「CSS 动画」**之前**（优先级更高），
      所以 `.bstar{opacity:.9!important;scale:0!important}` 会把两条动画全程按死：
      实测 getComputedStyle 恒为 opacity .9 / scale 0，星星一个都不显示。
      本项目通篇 `!important`，这里是唯一必须例外的位置。
   ⚠️ `.banner` 已有 overflow:hidden，星星越界会被裁掉，所以位置都留在 44%~90% 区间。 */
.banner-stars{position:absolute!important;inset:0!important;z-index:0!important;pointer-events:none!important}
.bstar{
  position:absolute!important;display:block!important;
  width:14px!important;height:14px!important;
  background:currentColor!important;
  clip-path:polygon(50% 0%,61% 39%,100% 50%,61% 61%,50% 100%,39% 61%,0% 50%,39% 39%)!important;
  filter:drop-shadow(0 2px 3px #0000001f)!important;
  opacity:.9;scale:1;
  animation:bstar-pop .72s cubic-bezier(.2,1.5,.4,1) both,bstar-twinkle 3.6s ease-in-out infinite!important;
}
.bstar-1{left:47%!important;top:13%!important;color:#FFD93D!important;width:17px!important;height:17px!important;animation-delay:.05s,1.0s!important}
.bstar-2{left:53%!important;top:78%!important;color:#FFFFFF!important;width:12px!important;height:12px!important;animation-delay:.18s,1.6s!important}
.bstar-3{left:62%!important;top:9%!important;color:#FF8FA3!important;width:21px!important;height:21px!important;animation-delay:.10s,2.1s!important}
.bstar-4{left:70%!important;top:84%!important;color:#6FD9EE!important;width:14px!important;height:14px!important;animation-delay:.30s,1.3s!important}
.bstar-5{left:79%!important;top:17%!important;color:#FFFFFF!important;width:11px!important;height:11px!important;animation-delay:.42s,2.6s!important}
.bstar-6{left:88%!important;top:70%!important;color:#FFD93D!important;width:18px!important;height:18px!important;animation-delay:.24s,1.9s!important}
.bstar-7{left:44%!important;top:88%!important;color:#FFFFFF!important;width:13px!important;height:13px!important;animation-delay:.55s,2.9s!important}
@keyframes bstar-pop{from{scale:0}60%{scale:1.18}to{scale:1}}
@keyframes bstar-twinkle{0%,100%{opacity:.35}50%{opacity:1}}
@media(prefers-reduced-motion:reduce){
  .bstar{animation:none!important;scale:1!important;opacity:.85!important}
}

/* 移动端：侧栏已是一条 sticky 顶条，顶栏若也 sticky 会叠成两条。
   让顶栏跟随内容滚动（与「登录按钮随滚动滚走」的既有约定一致）。 */
@media(max-width:760px){
  .hh-topnav{
    position:static!important;height:auto!important;min-height:0!important;
    flex-wrap:wrap!important;gap:8px!important;padding:10px 13px!important;
    /* ⚠️ 2026-09-20：base 规则改成了「rgba(255,255,255,.5) 白底 + backdrop-filter:blur(10px)」
       （对齐参考图的毛玻璃顶栏），那是**桌面端**的形态 —— 桌面端 banner 用
       `margin:-64px` 顶到顶栏底下，顶栏背后确实有渐变可以透出来。
       移动端不是这套：顶栏是 position:static、banner 是它下面的圆角卡片，
       两者不重叠 → 毛玻璃没有可模糊的内容，只会白白吃一次合成开销；
       而且 50% 白底会让页面底色（浅灰）透上来，品牌字对比度下降。
       这里退回改动前的「实白底、无模糊」，保持移动端观感不变。 */
    background-color:#FFFFFF!important;background-image:none!important;
    backdrop-filter:none!important;-webkit-backdrop-filter:none!important;
  }
  .hh-topnav-main{order:2!important;width:100%!important;flex:1 1 100%!important}
  .hh-topnav-side{order:1!important;width:100%!important;justify-content:flex-end!important;gap:8px!important}
  .hh-topnav .banner-search{width:auto!important;flex:1 1 auto!important}
  .hh-topnav .hh-actions{flex:0 0 auto!important}
  .hh-brand{min-height:0!important;margin:0!important}
  .hh-logo,.logo{width:36px!important;height:36px!important;flex:0 0 36px!important}
  .hh-logo svg,.logo svg{width:36px!important;height:36px!important}
  .hh-brand strong{font-size:15px!important}
  .banner{padding:28px 22px!important;margin-bottom:18px!important;min-height:0!important}
  .banner h1.banner-title{font-size:30px!important;margin-bottom:12px!important}
  .banner-copy>p.banner-sub{font-size:13px!important;line-height:1.6!important}
  .banner-title>span::before{height:8px!important;bottom:-1px!important;border-radius:3px!important}
  /* 珊瑚双段小标记：桌面端 22×26（top:-6px left:-8px），移动端标题 30px ≈ 61 的 0.49 倍，
     缩到 11×13（top:-3px left:-5px）。 */
  .banner-title>span:first-child::after{top:-3px!important;left:-5px!important;width:11px!important;height:13px!important}
  /* 第 3 枚倾斜珊瑚：桌面端 17×21（left:43.6% top:30px），移动端按 0.49 缩。 */
  .banner-title::after{top:15px!important;width:9px!important;height:11px!important;left:43.6%!important}
  /* 移动端 banner 是圆角卡片、高度只有 ~180px，装饰图缩到 112px 高并压暗。
     ⚠️ 2026-09-21 起装饰是位图 `img/banner-illust.webp`（1197×674，比例 1.776）：
        写 `auto 112px` → 宽 112×1.776 ≈ 199px，右缘贴住。
        **不要写死宽高两个值** —— 那样会把插画压扁。
        历史：换位图前是 viewBox 887×226 的内联 SVG（比例 3.925），
        同样用 `auto 112px`（宽 440px），注释里那句「插画占右侧 38%」已随位图作废。 */
  .banner:after{background-position:right -22px bottom!important;background-size:auto 112px!important;opacity:.55!important}
  /* 与桌面端一致：顶栏空态那 5 个页面不要顶部留白（原来 14px）。 */
  body:has(.hh-topnav) .container{padding-top:6px!important}
}
@media(max-width:600px){
  .banner:after{display:none!important}
}

/* 移动端 banner 文案：页面自身的 ≤600px 规则写的是
   `.banner-kicker,.banner-copy>p,.banner h1{display:none}`，
   前提是「banner 里还留着搜索框，文案让位给搜索」。搜索框已经搬进顶栏，
   这个前提消失 —— 而且设计稿 responsive.mobile 明确给了 bannerTitleSize:28px，
   说明移动端标题本就该显示。这里恢复标题/副标题，并采用规格的
   mobile bannerHeight:180px（原来的 240px 是桌面值，在移动端纯属浪费首屏）。
   ⚠️ 必须用 :not(.banner-kicker) —— 角标也是 .banner-copy 的 <p> 子元素，
      直接写 .banner-copy>p 会以 (0,1,1) 压过 ⑩ 那条 .banner-kicker(0,1,0)，
      把本该隐藏的角标重新显示出来。 */
@media(max-width:600px){
  .banner{min-height:180px!important;padding:24px 22px!important}
  .banner h1{display:block!important;margin-bottom:10px!important}
  .banner-copy>p:not(.banner-kicker){display:block!important;font-size:14px!important}
  .banner-copy{max-width:none!important}
}

/* ==========================================================================
   个人中心：退出登录迁到页内侧栏（2026-09-22）——从 theme3.css 手写区回填
   ========================================================================== */
/* 桌面 ≥761px：退出登录与导航按钮同款外观，推到侧栏横条最右端。
   页面内联那条 `.nav-btn.logout` 是给旧版竖排侧栏写的（margin-top + 顶部边框 +
   下缘圆角），横排里那两个属性会让按钮顶出一条莫名其妙的横线，显式清掉。 */
@media(min-width:761px){
  .layout>.side .nav-btn.logout{margin:0 0 0 auto!important;border:0!important;border-radius:10px!important}
}
/* 个人中心的顶栏是空态行（本页既无年级标签也无搜索框），整条收掉
   （移动端另有一条 sticky 品牌条兜底）。只针对 .hh-profile-page：
   其它空态页（5 个分类页）顶栏里还有登录/注册按钮。 */
body.hh-profile-page .hh-topnav.hh-topnav-empty{display:none!important}
/* 移动端侧栏是横向滚动的胶囊条，退出登录排在最后 —— 用 sticky 贴住滚动容器右缘，
   左侧渐隐 + 阴影和滑过的按钮分层。 */
@media(max-width:760px){
  .layout>.side .nav-btn.logout{position:sticky!important;right:0!important;margin:0 0 0 auto!important;border:0!important;border-radius:10px!important;padding-left:16px!important;background-image:linear-gradient(90deg,#FFFFFF00,#FFFFFF 15px)!important;box-shadow:-10px 0 12px -9px #2C2D3033!important}
}

/* ============================================================================
   侧栏样式（2026-09-21 按用户新给的参考稿「样式.txt」重做，248px 侧栏）
   数值来源见 scripts/sidebar_assets.py 的文件头注释（含「有意保留的差异」）。
   ============================================================================ */

/* --- 窄屏适配（2026-09-21 第二轮：用户「整个网页做不同分辨率/尺寸的适配，
        尤其是用户可交互的区域，比如侧边栏」）--------------------------------
   248px 是参考稿的落地值，但它只在 ≥1281px 视口下成立：
   761px 视口里 248px 占掉 32.6%，内容区只剩 513px（课件卡被压到 2 列、标题换行）。
   用户选定的方案是「流体收窄」，所以这里把侧栏宽做成 196 → 248 的 clamp。

   ⚠️ 只有**宽度与品牌行**跟着缩，导航项（52px 命中区 / 16px 字 / 20px 图标）
      一律保持参考稿规格 —— 导航才是「用户可交互的区域」，字号缩小会让它更难用。
      实测 196px 侧栏下导航项可用宽 162px、内容只占 130px（含选中态箭头 146px），
      余量 16px，**不需要**缩小。
      真正会溢出的只有品牌行（logo + 6 个中文字），以及个人卡的副文案。
      实测 248px 下品牌行本来就差 1px（scrollW 201 > clientW 199），
      所以窄屏必须同步缩 logo 与字号，否则溢出会从 1px 放大到 53px。

   ⚠️ 所有 clamp 的上下限都在 **761px / 1281px** 两个视口取值：
        变量            761px    1281px+   斜率（每 px 视口）
        --hh-sbw        196      248       0.1
        --hh-sbpad      17       24        0.0134615
        --hh-logow      44       57        0.025
        --hh-logoh      26       34        0.0153846
        --hh-brandfs    17       22        0.0096154
        --hh-brandlh    26       33        0.0134615
        --hh-brandgap   10       12        0.0038462
        --hh-avatar     34       40        0.0115385
      改其中一个的上限时，**必须**同步检查品牌行是否还放得下：
        可用宽 = --hh-sbw − 2×--hh-sbpad
        品牌行 = --hh-logow + --hh-brandgap + 6 × --hh-brandfs
      在 761 / 1024 / 1281 三档都要满足「品牌行 ≤ 可用宽」。

   ⚠️ 定义在 `:root`（而不是 `body:has(.hh-topbar)`）是**必须**的：
      `gen-theme3.py` 末尾那块「首页对照 Figma」里的 banner 规则
      （`calc((100vw - var(--hh-sbw)) * 0.25518)` 等）也在 `:root` 作用域下求值。
      侧栏一旦收窄而 banner 还按 248 算，横幅比例就会整体跑偏。
   ------------------------------------------------------------------------- */
:root{
  --hh-sbw:clamp(196px,calc(196px + (100vw - 761px) * 0.1),248px);
  /* 内容列宽度上限（2026-09-21 第九轮）。原来 `.container` 与首页的
     `.section/.catnav/.footer` 都写死 `max-width:1200px`（来自设计规格
     layout.contentMaxWidth=1200px），在 1920 下两侧各空 236px、2560 下各空 556px，
     而 banner 却是满宽 —— 内容看着像被挤在中间一条窄带里。
     改成流体：视口 ≤1552px 时算式结果仍 ≤1200，取 max 后恒为 1200，
     所以**定稿宽度及以下版式零变化**（容器本身仍比 1200 窄，是容器在约束）；更宽才逐步放宽，上限 2080px。
     0.92 = 内容区两侧各留 4% 呼吸位；下限 1200px 是「窄屏永远吃不到这个变量」的保险。 */
  --hh-content:min(2080px,max(1200px,calc((100vw - var(--hh-sbw)) * 0.92)));
  /* 「窄内容页」的宽度上限（2026-09-21 第九轮补）。联系页 `.contact-main` 写死
     `max-width:1040px`（`contact.html` 页内 <style>），2560 下两侧各空 636px ——
     和 `.container` 是同一个毛病，只是它的定稿值更窄。
     ⚠️ **不能直接复用 `--hh-content`**：那个下限是 1200px，而联系页的定稿值是 1040px，
       套上去会把 1440 下的联系页从 1040 撑到 1200 —— 那是改版式，不是适配。
       所以另开一个变量，下限仍取 1040px：视口 ≤1548px 时算式结果 ≤1040，取 max 后恒为 1040，
       1440 下与改造前逐像素相同；更宽才放宽，上限 1500px（联系页是一张表单卡，
       里面还有 300px 的固定二维码列，不该像列表页那样放到 2080）。
     0.8 = 内容区两侧各留 10%。 */
  --hh-content-narrow:min(1500px,max(1040px,calc((100vw - var(--hh-sbw)) * 0.8)));
  --hh-sbpad:clamp(17px,calc(17px + (100vw - 761px) * 0.0134615),24px);
  --hh-logow:clamp(44px,calc(44px + (100vw - 761px) * 0.025),57px);
  --hh-logoh:clamp(26px,calc(26px + (100vw - 761px) * 0.0153846),34px);
  --hh-brandfs:clamp(17px,calc(17px + (100vw - 761px) * 0.0096154),22px);
  --hh-brandlh:clamp(26px,calc(26px + (100vw - 761px) * 0.0134615),33px);
  --hh-brandgap:clamp(10px,calc(10px + (100vw - 761px) * 0.0038462),12px);
  --hh-avatar:clamp(34px,calc(34px + (100vw - 761px) * 0.0115385),40px);
}

/* --- 容器：宽 228 → 248（窄屏流体收窄到 196）、内边距 24、底色 #F5F5F5 ------ */
body:has(.hh-topbar){padding-left:var(--hh-sbw)!important}
.hh-topbar{
  width:var(--hh-sbw)!important;padding:var(--hh-sbpad)!important;
  background:#F5F5F5!important;backdrop-filter:none!important;
  border-right:1px solid #E8E9EC!important;
  /* ⚠️ 参考稿是 1360px 高的画布，而导航项放大到 52px 后整条侧栏在
     **< ~630px 视口高**时会超高（实测 600px 时账号卡底边 626 > 侧栏 600，被裁掉 ——
     账号卡是登录入口，裁掉等于小窗口下登录不可达）。所以给桌面侧栏一个纵向滚动条：
     高度够时它不出现、几何与参考稿完全一致；不够时才出现。 */
  overflow-y:auto!important;overflow-x:hidden!important;scrollbar-width:thin!important;
}

/* --- 品牌：logo 57×34（参考稿实测值），文字 22px/700 #262B38 ---------------- */
.hh-brand{gap:var(--hh-brandgap)!important;padding:0 0 20px!important;align-items:center!important}
.hh-logo,.logo{
  width:var(--hh-logow)!important;height:var(--hh-logoh)!important;flex:0 0 var(--hh-logow)!important;
  background:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 71 43%22%3E%3Ccircle cx=%2215.5%22 cy=%2220%22 r=%2216.5%22 fill=%22%23FECC1B%22/%3E%3Crect x=%2235.75%22 y=%223.75%22 width=%2230.5%22 height=%2230.5%22 rx=%224.5%22 fill=%22%232E7DE0%22 transform=%22rotate(16.7 51 19)%22/%3E%3Cpath d=%22M34 17.98 46.87 38.5H20.13z%22 fill=%22%23FF3D54%22 stroke=%22%23FF3D54%22 stroke-width=%226%22 stroke-linejoin=%22round%22/%3E%3Cg fill=%22%23FFFFFF%22%3E%3Cellipse cx=%229.6%22 cy=%2217.4%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 9.6 17.4)%22/%3E%3Cellipse cx=%2220%22 cy=%2216.3%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 20 16.3)%22/%3E%3Cellipse cx=%2229%22 cy=%2227.6%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 29 27.6)%22/%3E%3Cellipse cx=%2239%22 cy=%2229%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 39 29)%22/%3E%3Cellipse cx=%2249.5%22 cy=%2218%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 49.5 18)%22/%3E%3Cellipse cx=%2259.5%22 cy=%2217%22 rx=%221.5%22 ry=%222.05%22 transform=%22rotate(-12 59.5 17)%22/%3E%3C/g%3E%3Cg fill=%22none%22 stroke=%22%23FFFFFF%22 stroke-width=%221.7%22 stroke-linecap=%22round%22%3E%3Cpath d=%22M11 20q4.5 6 9 0%22/%3E%3Cpath d=%22M31.5 31.5q3.5 4.6 7 0%22/%3E%3Cpath d=%22M51 20q4 4.4 8 0%22/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat!important;
}
/* 旧的内联 <svg>（形状/配色/层序都不对）在 11 个页面里各有一份，且 4 个分类页是
   生成产物 —— 与其改 11 处标记，不如让新图形接管、把旧的藏掉。 */
.hh-logo svg,.logo svg{display:none!important}
.hh-brand strong{color:#262B38!important;font-size:var(--hh-brandfs)!important;font-weight:700!important;line-height:var(--hh-brandlh)!important}
/* --- 品牌位整体换图（2026-09-21）：logo 图标 + 站名文字合成一张 400×112 PNG，
   由页面标记里的单个 <img class="hh-brand-img"> 接管整行（上面的 .hh-logo /
   .hh-brand strong 规则保留 —— 登录弹窗的 .auth-logo.hh-logo 仍依赖 .hh-logo）。
   宽度跟随侧栏可用宽（--hh-sbw − 2×--hh-sbpad = 162 → 200px），高按 400:112 比例
   自动走（200px 宽时 56px 高，比旧 logo 行 34px 高约 22px，侧栏 overflow-y 会吸收）。
   下面的 strong 在顶栏已无对应节点，不删是为了少动一层历史规则。 */
.hh-brand .hh-brand-img{
  display:block!important;
  width:calc(var(--hh-sbw) - 2*var(--hh-sbpad))!important;
  height:auto!important;flex:0 0 auto!important;
}

/* --- 折叠按钮：20×20 描边图标（stroke-width 1.8 → 渲染 1.5px），无边框 ------ */
.hh-sidebar-toggle{
  width:28px!important;height:28px!important;padding:4px!important;
  margin:0 0 16px auto!important;border:0!important;border-radius:8px!important;
  background:transparent!important;color:#87888A!important;
}
.hh-sidebar-toggle svg{width:20px!important;height:20px!important;stroke-width:1.8!important}
.hh-sidebar-toggle:hover{color:#262B38!important;background:#ECECEC!important}

/* --- 导航：52px 高 / 16px 字 / padding 16 / 图标↔文字 gap 14 / 行距 12 ------ */
.hh-nav{gap:12px!important;font-size:16px!important;color:#87888A!important}
.hh-nav a{
  display:flex!important;align-items:center!important;
  padding:16px!important;border-radius:8px!important;gap:14px!important;
  font-size:16px!important;font-weight:500!important;color:#87888A!important;
  /* 行高钉成 20px = 图标高度：否则 16px 字的默认行高（~22px）会把内容盒撑到 22px，
     整项变成 54px（参考稿是 52px）。 */
  line-height:20px!important;
}
.hh-nav a:hover{color:#262B38!important;background:#FFF0F3!important}
/* 选中态：参考稿是 32px 圆角（52px 高的项 → 全圆胶囊）。主色沿用全站统一的 #FF4D6D。 */
.hh-nav a.active{
  color:#fff!important;background:#FF4D6D!important;
  border-radius:32px!important;font-weight:700!important;
}
/* 图标 20×20。2026-09-23 起改用**位图 <img class="hh-nav-ico">**（用户提供的 PNG，
   统一调成 #87888A，见 frontend/img/icons/）：未选中原色、hover 压深、选中翻白。
   mask 那套（::before）只留给没有位图的项 —— 目前只剩「课件管理」（admin 项，
   用户没给图），用 `:not(:has(img.hh-nav-ico))` 排除带位图的项。 */
.hh-nav a:not(:has(img.hh-nav-ico))::before{
  content:""!important;flex:0 0 20px!important;width:20px!important;height:20px!important;
  background:currentColor!important;
  -webkit-mask:var(--hh-icon) center/contain no-repeat!important;
  mask:var(--hh-icon) center/contain no-repeat!important;
}
/* 位图图标：盒 20×20 与参考稿一致，object-fit:contain 保比例（源图非正方形）。
   ⚠️ img 不吃 currentColor，三个状态全靠 filter：
     · 未选中：图片本身就是 #87888A，不加工；
     · hover（文字 #262B38）：brightness(0) 压黑再 invert(0.16) 提到 ~16% 亮度；
     · 选中（珊瑚底白字）：brightness(0) invert(1) 翻成纯白。 */
.hh-nav a img.hh-nav-ico{
  display:block!important;flex:0 0 20px!important;
  width:20px!important;height:20px!important;object-fit:contain!important;
}
.hh-nav a:hover img.hh-nav-ico{filter:brightness(0) invert(0.16)!important}
.hh-nav a.active img.hh-nav-ico{filter:brightness(0) invert(1)!important}
/* 右箭头只在选中项出现（参考稿只有「首页」那一行有）。盒 16px。 */
.hh-nav a::after{
  content:""!important;display:none!important;flex:0 0 16px!important;
  width:16px!important;height:16px!important;margin-left:auto!important;
  background:currentColor!important;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M8.6 4.6 16 12l-7.4 7.4%22/%3E%3C/svg%3E") center/contain no-repeat!important;
  mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M8.6 4.6 16 12l-7.4 7.4%22/%3E%3C/svg%3E") center/contain no-repeat!important;
}
.hh-nav a.active::after{display:block!important}
/* 「课件管理」要跟其余 5 项长得一模一样。它被 theme2-v2 当「按钮」处理过，有两处必须显式压掉：
     · `.hh-nav .hh-admin{display:block!important}` —— 这条把 flex 布局打断，于是图标位空着、
       文字缩进也跟其他项对不齐；
     · theme2-v2 第 19 行把 `.hh-admin` 归进「浅色按钮」那一组，给了白底 + 深字，
       与其余项（透明底 + 灰字）不一致。
   ⚠️ 上面两条**不要在这里写出原始色值**：生成器末尾有一份「旧主题色残留」检查（OLD 列表），
      注释里出现那些色值会被当成残留报出来，掩盖真正的问题。
   ⚠️ 选择器必须写到 (0,2,1)：`.hh-nav .hh-admin` 是 (0,2,0)、`.hh-admin` 是 (0,1,0)，
      两边都带 !important，只能靠特异性压下去。
   ⚠️ 只写 `.hh-nav a.hh-admin`（不要单独写 `.hh-admin`）—— 这样它待在 `.hh-actions` 里时
      （未登录 / 非管理员）仍然吃 `.hh-actions .hh-admin{display:none}`，不会被误显示出来。
   右侧的箭头不用另写：它已经是 `.hh-nav a`，`.hh-nav a.active::after` 会给它在 admin.html 上显示。

   ⚠️⚠️ 2026-09-23 补：上面那条 `display:flex!important` 把 `hidden` 属性**彻底压死**了 ——
      `[hidden]{display:none}` 是 UA 样式（0,1,0，且没有 !important），任何带 !important 的
      display 都能赢。而「课件管理」并不是只靠 CSS 藏：JS 侧 site-header.js 的
      `apply()` 用 `admin.hidden = !a.isStaff`、`applyLoggedOut()` 用 `admin.hidden = true`
      作为唯一闸门。于是只要这个节点待在 `.hh-nav` 里，闸门就形同虚设。
      实测（_verify/2026-09-23-nav-admin-gate）：非管理员账号在 profile.html / admin.html
      （这两页的 #adminAction **静态 HTML 就在 .hh-nav 里**）上，hidden=true 却 display:flex，
      侧栏照样显示「课件管理」；管理员在其它页登录后该节点被 JS 搬进 .hh-nav，
      再退出登录同样收不回去 —— 用户反馈的正是这条。
      ↓ 下面这条 (0,3,1) > (0,2,1) 且同带 !important，把 hidden 重新变成有效的闸门。 */
.hh-nav a.hh-admin{
  display:flex!important;align-items:center!important;
  color:#87888A!important;background:transparent!important;border:0!important;
  font-size:16px!important;font-weight:500!important;
  padding:16px!important;border-radius:8px!important;gap:14px!important;
}
.hh-nav a.hh-admin:hover{color:#262B38!important;background:#FFF0F3!important}
.hh-nav a.hh-admin.active{color:#fff!important;background:#FF4D6D!important;border-radius:32px!important;font-weight:700!important}
/* 「课件管理」只有管理员可见 —— 让 hidden 属性重新生效。
   ⚠️ 特异性必须 ≥ (0,3,1)（`.hh-nav a.hh-admin` 是 (0,2,1)），少写一段就会静默失效：
      症状是「非管理员也能看到」而不是任何报错。改这里请同步跑
      _verify/2026-09-23-nav-admin-gate/verify.py（含非管理员 / 退出登录两个用例）。 */
.hh-nav a.hh-admin[hidden]{display:none!important}
.hh-nav a:nth-child(1){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M1.6 9.5 12 1.5l10.4 8%22/%3E%3Cpath d=%22M1.6 9.5v9.6c0 2.2 4.7 3.4 10.4 3.4s10.4-1.2 10.4-3.4V9.5%22/%3E%3Cpath d=%22M7.6 17.4c1 1.2 2.5 1.8 4.4 1.8s3.4-.6 4.4-1.8%22/%3E%3C/svg%3E")}
.hh-nav a:nth-child(2){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M12 3.3v18.4%22/%3E%3Cpath d=%22M12 3.3H1.5M12 3.3h10.5%22/%3E%3Cpath d=%22M1.5 3.3v13.2c0 1.6 4.7 2.4 10.5 4.5%22/%3E%3Cpath d=%22M22.5 3.3v13.2c0 1.6-4.7 2.4-10.5 4.5%22/%3E%3Ccircle cx=%2212%22 cy=%228.4%22 r=%225%22/%3E%3Cpath d=%22M7 8.4h10%22/%3E%3Cpath d=%22M12 3.4c2.1 1.9 2.1 8.1 0 10M12 3.4c-2.1 1.9-2.1 8.1 0 10%22/%3E%3C/svg%3E")}
.hh-nav a:nth-child(3){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M12 2.8v20%22/%3E%3Cpath d=%22M8.8 6 12 2.8 15.2 6%22/%3E%3Cpath d=%22M2.4 2.8C3.6 8.8 6.7 14.8 11.5 17.5%22/%3E%3Cpath d=%22M21.6 2.8C20.4 8.8 17.3 14.8 12.5 17.5%22/%3E%3Cpath d=%22M2.9 20.3h1.8M6.5 20.3h1.8M14.8 20.3h1.8M18.4 20.3h2.7%22/%3E%3C/svg%3E")}
.hh-nav a:nth-child(4){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Crect x=%222.8%22 y=%221.9%22 width=%2218.4%22 height=%2213.8%22 rx=%223%22/%3E%3Cpath d=%22M7 7v5.5M15.2 7v5.5%22/%3E%3Cpath d=%22M9.7 9.7h4%22/%3E%3Cpath d=%22M7 16.1 4 21.7M11.1 16.1v4.2M15.2 16.1l3 5.6%22/%3E%3C/svg%3E")}
.hh-nav a:nth-child(5){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Crect x=%221.9%22 y=%222.8%22 width=%228.3%22 height=%227.4%22 rx=%221.8%22/%3E%3Crect x=%2212%22 y=%222.8%22 width=%229.2%22 height=%227.4%22 rx=%221.8%22/%3E%3Cpath d=%22M10.2 3.9h1.8%22/%3E%3Cpath d=%22M1.9 6.6H.9M21.2 6.6h1%22/%3E%3Crect x=%221.5%22 y=%2212.5%22 width=%2219.7%22 height=%226.8%22 rx=%221.5%22/%3E%3Cpath d=%22M16.6 12.5v6.8%22/%3E%3C/svg%3E")}
.hh-nav a:nth-child(6){--hh-icon:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Crect x=%222.5%22 y=%222.5%22 width=%227.6%22 height=%227.6%22 rx=%222%22/%3E%3Crect x=%2213.9%22 y=%222.5%22 width=%227.6%22 height=%227.6%22 rx=%222%22/%3E%3Crect x=%222.5%22 y=%2213.9%22 width=%227.6%22 height=%227.6%22 rx=%222%22/%3E%3Crect x=%2213.9%22 y=%2213.9%22 width=%227.6%22 height=%227.6%22 rx=%222%22/%3E%3C/svg%3E")}

/* --- 历史卡：200×114（padding 16 / 标题↔空态 gap 14 / 圆角 12 / 白底）------
   用 `flex:0 1 auto` 而不是写死高度：空态时贴着 114px（参考稿值）。
   ⚠️ 2026-09-21 第九轮：`max-height` 原来是**写死的 230px**，导致大屏也照样出滚动条 ——
     列表最多渲染 10 条（`site-header.js` 里 `rows.slice(0,10)`），
     10 条 = 10×28 + 9×6 = 334px，加标题 20 + 上间距 8 + 上下内边距 32 ≈ **394px**，
     永远塞不进 230px，所以**无论屏幕多高都必然出现滚动条**（用户原话：
     「大屏幕明明能够显示完全，却自动加了个滚动条」）。
   改成随视口高度自适应：`max(160px, 100vh − 560px)`。
     560px = 侧栏除历史卡外的固定占用（上下 padding 48 + 品牌行与导航 ≈406 + 卡片上下外边距 30
     + 个人卡 52 + 余量）。实测 1200 高时可用 664px、公式给 640px，安全。
   效果：视口高 ≥约 1000px 时 10 条全部显示、无滚动条；更矮才回落到「列表自己滚」——
     这正是「大屏不滚、小屏才滚」的期望行为。 */
.hh-sidebar-history{
  flex:0 1 auto!important;min-height:114px!important;
  max-height:max(160px,calc(100vh - 560px))!important;
  margin:20px 0 10px!important;padding:16px!important;
  border:0!important;border-radius:12px!important;
  background:#FFFFFF!important;box-shadow:0 2px 8px #0000000A!important;
}
.hh-history-title{
  display:flex!important;align-items:center!important;gap:15px!important;
  margin:0!important;color:#87888A!important;font-size:16px!important;font-weight:500!important;
}
/* 标题图标 2026-09-23 起用位图 <img class="hh-history-ico">（frontend/img/icons/nav-history.png）；
   ::before 的 mask 只留给没有位图的标题（兜底）。 */
.hh-history-title:not(:has(img.hh-history-ico))::before{
  content:""!important;flex:0 0 20px!important;width:20px!important;height:20px!important;
  background:#87888A!important;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Ccircle cx=%2212%22 cy=%2212%22 r=%2210.5%22/%3E%3Cpath d=%22M12 6.5V12l2.8 2.8%22/%3E%3C/svg%3E") center/contain no-repeat!important;
  mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%23000%22 stroke-width=%221.8%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Ccircle cx=%2212%22 cy=%2212%22 r=%2210.5%22/%3E%3Cpath d=%22M12 6.5V12l2.8 2.8%22/%3E%3C/svg%3E") center/contain no-repeat!important;
}
.hh-history-title img.hh-history-ico{
  display:block!important;flex:0 0 20px!important;
  width:20px!important;height:20px!important;object-fit:contain!important;
}
/* 「登录后查看浏览记录」在参考稿里是**居中**的，且贴近卡片底部。 */
.hh-history-empty{
  display:block!important;margin-top:auto!important;padding:0 0 2px!important;
  color:#87888A!important;font-size:12px!important;text-align:center!important;
}
/* 历史记录项：每条固定成独立行，禁止 flex shrink 把文字挤到同一层。
   额外的 min-height / flex-shrink:0 / line-height 让真实接口返回多条记录时也保持清晰间距。 */
.hh-history-list{gap:6px!important;margin-top:8px!important;min-height:0!important;overflow-y:auto!important;
  scrollbar-width:thin!important;scrollbar-color:#DCDCDC transparent!important}
/* 滚动条美化（2026-09-21 第九轮，用户反馈「这个滚动条不好看」）。
   默认滚动条又宽又带两端按钮，在 199px 宽的卡片里尤其扎眼。
   ⚠️ 两套写法都要给，缺一套就有一类浏览器回退到默认样式：
     · `scrollbar-width` / `scrollbar-color` → Firefox + 新版 Chromium
     · `::-webkit-scrollbar*` → Safari + Chromium（Chromium 两套都认，以后者为准）
   轨道透明、滑块 6px 圆角浅灰、悬停加深。刻意**不用** `scrollbar-gutter:stable` ——
   那会常年占掉 6px 宽度，而这个卡片本来就只有 199px。 */
.hh-history-list::-webkit-scrollbar{width:6px!important;height:6px!important}
.hh-history-list::-webkit-scrollbar-track{background:transparent!important}
.hh-history-list::-webkit-scrollbar-thumb{background:#DCDCDC!important;border-radius:99px!important}
.hh-history-list::-webkit-scrollbar-thumb:hover{background:#C2C2C2!important}
/* 侧栏容器自身（`overflow-y:auto`）在窄屏内容超高时也会出滚动条，一并统一。 */
.hh-topbar{scrollbar-width:thin!important;scrollbar-color:#DCDCDC transparent!important}
.hh-topbar::-webkit-scrollbar{width:6px!important}
.hh-topbar::-webkit-scrollbar-track{background:transparent!important}
.hh-topbar::-webkit-scrollbar-thumb{background:#DCDCDC!important;border-radius:99px!important}
.hh-history-list a{display:block!important;flex:0 0 auto!important;min-height:28px!important;box-sizing:border-box!important;padding:6px 8px!important;font-size:12px!important;line-height:16px!important;color:#87888A!important;background:#FAFAF8!important;border:1px solid #EEEEEA!important;border-radius:8px!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;text-align:center!important}
.hh-history-list a:hover{background:#FFF0F3!important;border-color:#FFD4DD!important;color:#262B38!important}

/* --- 个人卡：200×52 / gap 10 / 无底色；头像 40×40 圆形 ----------------------
   参考稿的个人卡是「裸行」（无底色、无描边），只在 hover 时给一层浅灰反馈。 */
.hh-profile-card{
  margin-top:auto!important;padding:6px 8px!important;
  border:0!important;background:transparent!important;border-radius:12px!important;
  gap:10px!important;
}
.hh-profile-card:hover{background:#ECECEC!important;box-shadow:none!important}
.hh-profile-avatar{
  width:var(--hh-avatar)!important;height:var(--hh-avatar)!important;flex:0 0 var(--hh-avatar)!important;
  border-radius:50%!important;background:#FF4D6D!important;color:#fff!important;
  font-size:18px!important;font-weight:700!important;
}
/* 卡片本身已经给了 gap:10px，副本的 margin-left 要归零，否则会叠成 18px。 */
.hh-profile-card-copy{margin-left:0!important;min-width:0!important;overflow:hidden!important}
/* ⚠️ 窄屏（侧栏收到 196px）时这行副文案「登录后进入个人中心」放不下：
   实测卡片可用宽 146px，而 头像 34 + gap 10 + 文案 108 + gap 10 + 箭头 8 = 170px。
   `flex:0 1 auto` 的副本默认 `min-width:auto` 不会收缩，于是整张卡右溢 24px。
   这里让两行文字都能省略号截断 —— 248px 下它们本来就放得下，**不受影响**。
   ⚠️⚠️ 2026-09-21（同日第六轮）修正：上面这招把「右溢 24px」换成了「文字被截」，
   而截断窗口比当时想的宽得多 —— 实测需要 108px，可用宽只有：
     761 → 86（超 22，只显示 67%，肉眼是「登录后进入个…」）
     900 → 94（超 14）· 1024 → 101（超 7）· 1440 → 108（刚好放下）
   根因是**侧栏流体收窄 248→196px 本身**（改版前恒 248px，这行从来放得下）。
   → `small` 改为**允许折行**（见下面 small 那条），`strong`（用户名）保持单行省略号
     （用户名可能很长，折行反而难看）。
   折行实测代价：卡片 51 → 68px，且**向上长**（卡底仍在 883 不变，`margin-top:auto`
   吸收）；`.hh-topbar`（fixed + overflow-y:auto）scrollHeight 仍 900、overflow 0
   → 不产生侧栏滚动条；1440 下仍是单行 52px，完全不受影响。 */
.hh-profile-card-copy strong,.hh-profile-card-copy small{
  display:block!important;min-width:0!important;
  overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;
}
.hh-profile-card-copy strong{color:#87888A!important;font-size:14px!important;font-weight:700!important}
.hh-profile-card-copy small{margin-top:2px!important;color:#87888A!important;font-size:12px!important;white-space:normal!important;text-overflow:clip!important}
.hh-profile-card-arrow{color:#87888A!important;font-size:16px!important}

/* --- 收起态：侧栏 78px（内容宽 54px），57px 宽的 logo 会溢出，收窄到 44×26 -- */
.hh-sidebar-collapsed .hh-logo,.hh-sidebar-collapsed .logo{
  width:44px!important;height:26px!important;flex:0 0 44px!important;
}
/* 收起态侧栏内容宽 54px：合成图按宽度缩（54px 宽 → 约 15px 高，站名仍可辨认原图比例）。 */
.hh-sidebar-collapsed .hh-brand .hh-brand-img{width:54px!important}

/* 移动端 ≤760px：桌面那套「248px 固定侧栏」必须整体让位 —— 移动端顶栏是 100% 宽、60px 高的横条。
   ⚠️ 这几条**必须写在这里**（在本文件桌面规则的后面）：同特异性 + 同 !important 时靠源码顺序决胜，
      写在前面会被上面那套桌面规则压掉（实测：漏了 padding-left 时移动端整页被推右 248px、
      顶栏只剩 248px 宽）。theme2-v2 的移动端规则在更前面，同样压不住后写的桌面规则。 */
@media(max-width:760px){
  body:has(.hh-topbar){padding-left:0!important}
  .hh-topbar{
    width:100%!important;height:60px!important;min-height:60px!important;
    padding:0 13px!important;border-right:0!important;background:#FFFFFFF5!important;
    overflow:visible!important;
  }
  /* ⚠️ 命中区：移动端顶条里的品牌链接是**整条里唯一的「回首页」入口**
     （`.mobile-dock` 也有首页项，但顶条这个更顺手）。它原来只有文字行盒那么高 ——
     实测 `a.hh-brand` **140×26px**（logo 40×24、`strong` 行盒 26），而顶条本身 60px，
     上下各 17px 全是点不到的死区。撑到 44px（iOS 触屏建议的最小值）。
     顶条是 `align-items:center`、内容区高 60px，44px 的子项不会引起任何位移。 */
  .hh-brand{width:auto!important;padding:0!important;gap:10px!important;min-height:44px!important}
  .hh-sidebar-toggle{display:none!important}
  .hh-logo,.logo{width:40px!important;height:24px!important;flex:0 0 40px!important}
  .hh-brand strong{font-size:15px!important}
  /* 移动端顶栏高 60px：合成图若沿用桌面的「按可用宽」算式会吃掉整条顶栏
     （100vw−26px），改为按高定宽（30px 高 → 约 107px 宽），与旧 140×26 品牌行同量级。 */
  .hh-brand .hh-brand-img{width:auto!important;height:30px!important}
}

/* --- 收起态个人卡：头像真正居中（2026-09-21）--------------------------------
   现象：收起时头像圆片左移、溢出卡片左缘 3px（实测 offset −3px）。
   根因：copy/arrow 被前面的收起规则压成 max-width:0 但仍 display:flex/block，
   卡片自身的 gap:10px 还在 —— 内容总宽 = 40(头像) + 10 + 0(copy) + 10 + 0(arrow)
   = 60px，塞进 43px 的内容盒（卡片 53 − 左右 padding 5×2），
   justify-content:center 会带着溢出量一起「居中」，于是整组左移出界。
   → 收起态把卡片 gap 归零、头像用 margin:0 auto 自居中（copy/arrow 仍是
     opacity:0 + 0 宽，不参与布局）。 */
.hh-sidebar-collapsed .hh-profile-card{gap:0!important;padding-left:5px!important;padding-right:5px!important}
.hh-sidebar-collapsed .hh-profile-avatar{margin:0 auto!important}


/* ============================================================================
   首页对照 Figma「首页 / 珊瑚红方案」(1:326)：
   年级 + 搜索 + 登录叠在通栏 Banner 顶部；内容区白底；「全部课件」标题行。
   ============================================================================ */
html,body{background:#FFFFFF!important}
.banner-kicker{display:none!important}
.grade-filter:not(:has(.grade-chips)){display:none!important}
.section-title{
  margin:0!important;color:#2C2D30!important;
  font-size:22px!important;font-weight:700!important;line-height:1.3!important;
}
.section-head{
  display:flex!important;justify-content:space-between!important;align-items:center!important;
  gap:12px!important;margin:8px 0 16px!important;
}
body:has(.hh-topnav:not(.hh-topnav-empty)) .tabs{
  border:0!important;background:transparent!important;gap:8px!important;padding:0!important;
}
body:has(.hh-topnav:not(.hh-topnav-empty)) .tabs button{
  border:0!important;border-radius:8px!important;padding:6px 16px!important;
  background:#F7F8FA!important;color:#6B7280!important;font-weight:500!important;
}
body:has(.hh-topnav:not(.hh-topnav-empty)) .tabs button.active{
  background:#FF4D6D!important;color:#FFFFFF!important;
}

/* 首页资源类型入口：必须显式恢复 grid；否则基础样式中的 ul/list-item 会把卡片
   竖向拉成巨高区域，看起来像“跑到页面底部”。
   列数：与侧边栏一级导航一一对应（交互网页 / 图解视频 / 互动课件 / 教师工具）。
   ⚠️ 2026-09-21 第二轮：`repeat(4,1fr)` → `repeat(auto-fit,minmax(250px,1fr))`。
      固定 4 列**不看容器宽度**，只由视口断点（≤900px 换 2 列）兜底，于是
      901–1540px 之间一直是 4 列，卡片被压到放不下说明文字：
        实测 1024 视口：内容列 776 − 48 = 728，4 列每张 171px；
        扣掉 padding 36 / 图标 46 / 两处 gap 28 / 箭头 11 → `.cat-info em` 只剩 **41px**，
        描述被截成「可点…」「画面…」「像真…」「备课…」。
      改 auto-fit 后列数由**可用宽度**决定，与侧栏收窄/展开自动联动：
        内容 1144px → 4 列（1440 视口，与定稿一致）· 984 → 3 列（1280）
        728 → 2 列（1024）· 517 → 2 列（761）
      250px 这个下限是按「卡片 = 18×2 padding + 46 图标 + 14 gap + 内容 + 11 箭头」
      反推的：内容要放得下 15 个字的描述（12px × 15 = 180px）→ 卡片 ≥ 301px 才一行放完，
      但配合下面 `em` 允许两行后，250px 也读得通，故取 250 让 1440 视口仍是 4 列。
   ⚠️ 改列数时 index.html 的 .cat-card 数量必须同步改，否则会出现空轨道。 */
.catnav{margin:28px 0 8px!important;padding:0!important}
.catnav-head{display:flex!important;flex-wrap:wrap!important;align-items:baseline!important;justify-content:space-between!important;margin:0 0 16px!important;gap:4px 16px!important}
.catnav-title{margin:0!important;font-size:22px!important;font-weight:700!important;color:#2C2D30!important;letter-spacing:-.02em!important}
.catnav-sub{margin:0!important;color:#9CA3AF!important;font-size:13px!important}
.catnav-grid{list-style:none!important;margin:0!important;padding:0!important;display:grid!important;grid-template-columns:repeat(auto-fit,minmax(250px,1fr))!important;gap:14px!important}
.cat-card{display:flex!important;align-items:center!important;gap:14px!important;min-width:0!important;padding:18px!important;border:1px solid #E8E9EC!important;border-radius:16px!important;background:#FFFFFF!important;cursor:pointer!important;transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease!important}
.cat-card:hover{transform:translateY(-3px)!important;border-color:#FF8FA3!important;box-shadow:0 14px 30px #FF4D6D14!important}
.cat-icon{flex:0 0 46px!important;width:46px!important;height:46px!important;display:grid!important;place-items:center!important;border-radius:12px!important;background:#FFF8FA!important}
.cat-icon svg{display:block!important;width:32px!important;height:32px!important}
.cat-info{flex:1 1 auto!important;min-width:0!important;display:flex!important;flex-direction:column!important;gap:4px!important}
.cat-info strong{display:block!important;font-size:15px!important;font-weight:700!important;color:#2C2D30!important;line-height:1.2!important}
/* ⚠️ 2026-09-21 第三轮：`white-space:nowrap` 单行省略号 → `-webkit-line-clamp:2` 两行。
   第二轮保留了单行，理由是「最坏 145/180 = 显示 81%，不构成问题」。用 canvas
   measureText + 二分复量后这个估算被推翻 —— 最坏的不是 15 字的描述，而是 19 字的
   「像真实 PPT 一样逐页翻阅的课堂演示」（need 202px）：
     761 视口 2 列档：avail 121 → 只显示 60%，最严重
     1280 视口 3 列档：avail 189 / need 202
     1440 视口 4 列档：avail 145 → 显示 71%（可见「像真实 PPT 一样逐页翻…」）
     1600 / 1920 与 1440 同（版式在 1600 以上冻结，avail 不再增长，1920 也只有 147）
   即 1440 定稿宽度下 4 张卡有 3 张被截（交互网页 182 / 互动课件 202 / 教师工具 182）。
   决定性依据：上面 250px 下限的推导写的就是「配合下面 em 允许两行后，250px 也读得通」
   —— **两行才是本来的设计意图**，nowrap 反而与自己的推导自相矛盾。
   实测代价：卡片 84 → 94px（+10px；上一轮估的 82→99 偏大）。宽列档不受影响，
   375 / 480 / 1024 仍是一行 84px —— 放得下就不折行。
   9 个宽度 375→1920 全量复验：截断 = 0，卡片高度只出现 84 / 94 两档。
   想退回单行：把本行换回 display:block + white-space:nowrap 即可。 */
.cat-info em{display:-webkit-box!important;-webkit-box-orient:vertical!important;-webkit-line-clamp:2!important;font-style:normal!important;font-size:12px!important;color:#9CA3AF!important;line-height:1.4!important;overflow:hidden!important;text-overflow:ellipsis!important}
.cat-arrow{flex:0 0 auto!important;font-size:18px!important;color:#C4C9D0!important;transition:transform .18s ease,color .18s ease!important}
.cat-card:hover .cat-arrow{transform:translateX(3px)!important;color:#FF4D6D!important}
@media(max-width:900px){.catnav-grid{grid-template-columns:repeat(2,1fr)!important}}
@media(max-width:480px){.catnav-grid{grid-template-columns:1fr!important}.catnav-head{flex-direction:column!important;align-items:flex-start!important;gap:4px!important}}

@media(min-width:761px){
  body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav{
    position:relative!important;z-index:5!important;
    /* 2026-09-21（用户明确要求）：底色的演变史，三代都记下来，别再绕回去。
       ── 第一代（base 规则）：`rgba(255,255,255,.5)` 白底 + `backdrop-filter:blur(10px)`。
       ── 第二代：用户说「透明度还是不够低，我要的效果是它透明到能够看到大方块」
          → 把白底与 blur **一起按死**，改成 `transparent`。当时记录的理由是：
            · 只关背景、留着 blur → 顶栏区仍是一片被糊平的色块，和下面 banner 的
              渐变接不上，看起来就是「一条脏带」；
            · 只关 blur、留着 50% 白 → 整条顶栏比 banner 亮一大截
              （实测 banner #eed6a0 被抬到 #f9f2ce，Δmax≈46）。
       ── 第三代（本次）：用户明确给出声明块，底色改成 **`rgba(255,255,255,0.3)`**
          —— 30% 白，**blur 仍然关闭**、`background-image` 仍然 `none`。
          ⚠️ **与当年那版「降到 .3」不是同一件事**：当年难看的是「.3 白 + blur(10px)」
            那个组合（白底把糊平的色块托起来 = 脏带）；这次 blur 是关的，
            所以只是给 banner 的渐变蒙一层 30% 白纱 —— 是「提亮并压低对比」，
            不是「糊平」。
          ⚠️ 保留 `backdrop-filter:none` 与 `background-image:none` 是**用户给的声明块
            里的原话**，不要因为「反正有底色了」就把 blur 加回来。
       ⚠️ 首页顶栏是 `position:relative`（不是 sticky），**不会**压着滚动内容，
          背后永远只有 banner 自己，所以不需要毛玻璃兜底可读性。
       ⚠️ 非首页顶栏（`.hh-topnav-empty`）走另一条规则，本来就是 transparent，不受影响。
       ⚠️ 这条在 `@media(min-width:761px)` 里，且选择器带 `:not(.hh-topnav-empty)`
          → 只作用于「≥761px 的首页顶栏」。 */
    background-color:rgba(255,255,255,0.3)!important;
    background-image:none!important;
    backdrop-filter:none!important;-webkit-backdrop-filter:none!important;
    border:0!important;box-shadow:none!important;
  }
  body:has(.hh-topnav:not(.hh-topnav-empty)) .container{
    max-width:none!important;padding:0 0 56px!important;
  }
  body:has(.hh-topnav:not(.hh-topnav-empty)) .banner{
    /* ⚠️⚠️ **2026-09-21（阶段 L）：整块 banner 改成「按宽度等比缩放」。**
       参考稿是一张固定画布（1352×345，比例 3.919）。要 1:1 复刻，就不能让高度
       写死在 343px —— 那样在 1440 视口下横幅只有 1192 宽、文案却还是 1600 视口的尺寸，
       文案必然压到插画上。所以高度、内边距、字号全部由**横幅宽 = 100vw − 248px** 推出。

       参考稿换算（分母 1352 / 345）：
         横幅高      345/1352 = 0.25518
         文案块顶    86/1352  = 0.0636   （参考稿标题墨迹顶 y96，减行高半余量与字形上留白 ≈10px）
         左内边距    257/1352 = 0.19009  （参考稿标题墨迹左缘 x257）
       由此得到的横幅高 = 0.25518 × 横幅宽，在 1600 视口下正好 345px（≈ 原 343）。

       ⚠️ **`align-items` 必须从 center 改成 flex-start**：等比之后「文案块顶」是个
          绝对比例（86/1352），再靠居中算就永远差一截。改成 flex-start 后
          `padding-top` 直接就是文案块顶，改字号时不用再联立方程。

       ⚠️ 三个 `clamp()` 的下限不是装饰：视口 761px 时横幅只剩 513 宽，
          纯比例会算出 131px 高的横幅 —— 比顶栏（64px）高不了多少，标题会整块
          藏到顶栏后面。下限 76px 保证最窄桌面档文案顶也压在顶栏之下。

       ⚠️⚠️ **2026-09-21 第二轮：高度改成 `height:auto` + `min-height`，下限 280 → 210。**
          原先是固定 `height:clamp(280px, …)`，于是「文案实际高度」从不参与计算：
            · 下限 280 在 761–1345px 区间一直生效，横幅被撑得**比比例更方**；
            · 而插画是按**横幅高度**定尺的（`auto 100%`，宽 = 高 × 1197/674 = 1.776 倍），
              横幅越方 → 插画越宽 → 标题压到插画上。
              实测 1024 视口：横幅 776×280，插画 497 宽（占 64%），标题右缘 x696
              比插画左缘 x527 多出 170px，其中 21px 落在**不透明**区域（左侧羽化只有 149px）。
              761 视口更糟：插画 497 宽 / 横幅 513 宽 = 97%，标题重叠 295px。
          改成 `min-height` 后，高度 = max(比例下限, 文案实际高度)：
            · 1345px 以上仍是 345px 上限，1600 视口的已定稿排版**完全不变**；
            · 1024 视口高度落到比例值 205px，插画缩到 364 宽，标题重叠降到 21px
              且全部落在羽化区内（不透明区从 x=438+109=547 才开始，标题右缘 459）。
            · 顺带消除一类隐患：改顶栏行数 / 改字号都不会再把文案裁掉
              （`.banner` 是 `overflow:hidden`，固定高度下多出来的文案是直接被切掉的）。 */
    margin:-64px 0 28px!important;
    height:auto!important;
    min-height:clamp(210px,calc((100vw - var(--hh-sbw)) * 0.25518),345px)!important;
    align-items:flex-start!important;
    padding:clamp(76px,calc((100vw - var(--hh-sbw)) * 0.06657),90px)
            0 0
            calc((100vw - var(--hh-sbw)) * 0.19009)!important;
    border-radius:0!important;box-shadow:none!important;
  }
  body:has(.hh-topnav:not(.hh-topnav-empty)) .section,
  body:has(.hh-topnav:not(.hh-topnav-empty)) .catnav,
  body:has(.hh-topnav:not(.hh-topnav-empty)) .footer{
    /* ⚠️ 2026-09-21 第九轮：与 `.container` 同一处理 —— 写死的 1200px 换成 `--hh-content`。
       首页的 banner 是满宽（container 被放开成 max-width:none），如果这里还锁 1200，
       横幅与下方内容就会明显不成比例（1920 下内容两侧各空 236px）。 */
    max-width:var(--hh-content)!important;margin-left:auto!important;margin-right:auto!important;
    padding-left:24px!important;padding-right:24px!important;box-sizing:border-box!important;
  }
  body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav .banner-search{
    /* ⚠️ 2026-09-21 第二轮：固定 240px → 可压缩（下限 168px）。
       固定宽度是「年级标签被挤成 94px」的另一半原因：`.hh-topnav-side` 是
       `flex:0 0 auto`，搜索框又不可压缩，于是全部压力落到 `.hh-topnav-main`。
       改成可压缩后，761px 视口下搜索框让出 72px 给年级标签。
       ⚠️ 下限 168px 是「还能输入」的底线：168 − 30 左内边距 − 46 按钮 = 92px 输入区。
       写 `width:auto` 是必须的 —— `width` 会盖过 `flex-basis`，留着 240px 就永远不缩。 */
    width:auto!important;flex:0 1 240px!important;min-width:168px!important;
    height:36px!important;
    padding:0 0 0 36px!important;
    background:#FFFFFF url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%239CA3AF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2211%22%20cy%3D%2211%22%20r%3D%227%22%2F%3E%3Cpath%20d%3D%22M20%2020%2016.5%2016.5%22%2F%3E%3C%2Fsvg%3E") 12px 50%/16px 16px no-repeat!important;
    box-shadow:0 2px 8px #00000014!important;
  }
  /* ⚠️ 侧栏 `.hh-topnav-side` 只放开 `flex-shrink`，**不要**加 `min-width:0`。
     加了 `min-width:0` 会把它的下限整个去掉：搜索框停在 168px 之后侧栏还会继续收缩，
     登录按钮就被挤出顶栏（实测 761px 溢出 64px、900px 溢出 15px，按钮右半截被切掉）。
     保留默认的 `min-width:auto` 后，下限 = 168 + 12 + 按钮宽（761px 下 265px），
     顶栏内部因此始终闭合。 */
  body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav-side{flex:0 1 auto!important}
}

/* ============================================================================
   窄桌面档（761–1000px）：顶栏别把「年级标签」挤成一条 94px 的缝
   ----------------------------------------------------------------------------
   实测 761px 视口（侧栏收窄后内容列 565）：
     · `.hh-topnav` 内宽 517，`.hh-topnav-side`（搜索 240 + 账号按钮 103）固定占 355，
       留给 `.hh-topnav-main`（8 枚年级标签，内容 620px）的只有 **94px** —— 只看得见
       「全部年级」一枚，其余全在滚动区外。
     · 1024px 视口也只有 357px，第 5 枚标签被切掉一半（「四」字露一半）。
   根因：`.hh-topnav-side` 是 `flex:0 0 auto`（**不可压缩**），所以压力全落到
   `.hh-topnav-main` 上，而它 `min-width:0` 可以一路缩到 0。

   这里做三件事，都不改变「一行顶栏」的形态（换成两行会连带 banner 的
   `margin:-64px` / `padding-top` 一起改，风险大得多）：
     ① 顶栏内边距 24 → 18（+12px）
     ② 搜索框可压缩（下限 168px）—— 已在上面 `@media(min-width:761px)` 里放开，
        这里只把左内边距与放大镜位置一起缩，让 168px 里能多留点输入区
     ③ 年级标签与登录按钮缩一号（标签 8 枚 620 → 470px，滚动窗口里能多看到 2 枚）
   结果：761px 下 `.hh-topnav-main` 从 94px 涨到 ~248px，且顶栏内部不再溢出。

   ⚠️ 账号按钮显式 `flex:0 0 auto` 钉住，保证被压的永远是搜索框而不是登录入口。
   ⚠️ 选择器必须写成 `body:has(...) .hh-topnav .banner-search`（0,4,1）—— 与上面
      `@media(min-width:761px)` 里那条覆盖同特异性，靠**源码顺序**（本块在其后）决胜。
      少写 `body:has(...)` 就只剩 (0,2,0)，会被那条压掉。
   ⚠️ **不要**在这里给 `.hh-topnav-side` 加 `min-width:0`（已踩过一次）：那会去掉侧栏的
      下限，搜索框停止收缩后侧栏继续缩，登录按钮直接被挤出顶栏。
   ============================================================================ */
@media(min-width:761px) and (max-width:1000px){
  .hh-topnav{padding:0 18px!important}
  body:has(.hh-topnav:not(.hh-topnav-empty)) .hh-topnav .banner-search{
    padding:0 0 0 30px!important;
    background-position:10px 50%!important;
  }
  .hh-topnav .banner-search button{padding:0 12px!important}
  .hh-topnav .grade-chips .grade-chip{padding:8px 11px!important;font-size:13px!important}
  .hh-topnav .hh-actions .hh-login{padding:8px 13px!important;font-size:13px!important}
}
@media(max-width:600px){
  body:has(.hh-topnav:not(.hh-topnav-empty)) .section-head{display:flex!important}
}

/* ============================================================================
   Banner 整图「零裁切」修正（2026-09-21 第十轮）
   ----------------------------------------------------------------------------
   事实：banner-home.jpg 是 1920×489 → 比例 3.9257。而 banner 盒子的高度一直被
   `min-height:clamp(210px,(100vw − sbw)×0.25518,345px)` 决定，0.25518 只在 1600
   视口附近等于图片比例 —— 盒子一旦比图片「扁」或「方」，`object-fit:cover`
   就必然裁图（`.banner` 是 overflow:hidden，裁掉的部分再也看不到）：

     视口   banner 盒子   盒子比例   实测裁切
     1920   1672×345     4.85      上下各 40px（共 81px）← 用户报的「底部被切」
     1600   1352×345     3.92      0（设计基准档，唯一吻合的宽度）
     1024    802×210     3.82      左右各 11px
      900    690×210     3.29      左右各 67px
      761    565×210     2.69      左右各 130px（标题左缘被切掉）
      760    716×56     12.78      上下各 63px（min-height:0 把它压成一条细缝）
      600    572×180     3.18      左右各 68px
      390    362×180     2.01      左右各 172px（近一半画面被切）

   两处成因就是 min-height 的 **345 上限** 与 **210 下限**。它们当初服务的是
   「banner 里的 HTML 文案不被裁」（固定高度会把多出来的文案切掉）；2026-09-21
   文案已改成图片自带（`.banner-copy` 已删除），这两个限制失去了意义，只剩
   「把背景图裁掉」这一个副作用。

   修法：让盒子比例恒等于图片比例 —— `aspect-ratio:1920/489`，并把 min-height
   归零。此后 cover 与 contain 等价：任意宽度都完整显示，高度仍随宽度等比缩放
   （1600 视口算得 344.4px，与原来的 345px 基本一致，设计基准档排版本不变）。

   ⚠️ 必须写两条选择器，缺一不可：
      · `body:has(.hh-topnav:not(.hh-topnav-empty)) .banner`（0,3,1）与 desktop
        那条**同特异性**，靠源码顺序决胜 → 本块必须放在文件最后；
      · `.banner.banner`（0,2,0）压掉三条 `.banner` (0,1,0) 的 min-height
        （stage-1 的 `min-height:0`、≤600 与 ≤760 的 `180px`）。
   ⚠️ border-box 下 padding-top（76..90px）不再参与盒高计算：盒高现在完全由
      aspect-ratio 决定，padding 只影响「内容若有内容」的落点。
   ⚠️ 不要因为「反正 cover 兜底」就把 aspect-ratio 删掉 —— 没有它，广屏（>1600）
      与窄屏（≤1024）会立刻退回上面那张表的裁切状态。
   ⚠️ 平板上 banner 会自动落到 ~182px（760 视口），正好接近设计稿的
      responsive.mobile bannerHeight:180px；手机上（390）则是 92px 的通栏图。
   ============================================================================ */
.banner.banner{
  aspect-ratio:1920/489!important;
  height:auto!important;
  min-height:0!important;
}
body:has(.hh-topnav:not(.hh-topnav-empty)) .banner{
  aspect-ratio:1920/489!important;
  height:auto!important;
  min-height:0!important;
}

/* ============================================================================
   登录弹窗 logo 去白底盘（2026-09-21）
   ----------------------------------------------------------------------------
   `.auth-logo` 的白底（99 透明度的半透明白 + 投影，映射自 theme2-v2 的暖白底）是给旧实现 `<span class="hh-logo">` 托底用的 —— 那时弹窗的品牌
   图形是 theme3 的侧栏背景图色块，必须垫一块白板才看得清。
   2026-09-21 弹窗 logo 改成 `<img src="./assets/brand.png">`（400×112 透明 PNG；
   实测 alpha=0 占 73.9%，不透明白色只剩图标笑脸的 172px，文字白孔本来就已
   镂空），这层白底盘反而把透明度全部盖住 —— 在渐变面板上渲染成一块白补丁。
   这里关掉背景与投影；border-radius 一并归零是防御性的（img 内容不贴角，
   但 theme2-v2 遗留体系给过 13px 圆角，别让它有机会裁到图）。
   ⚠️ 只作用登录弹窗的 .auth-logo；侧栏的 .hh-logo / .hh-brand 白底不受影响。
   ============================================================================ */
.auth-logo{
  background:none!important;box-shadow:none!important;
  border:0!important;border-radius:0!important;
}
