:root { --portal-bg: #f3f7fb; --portal-surface: #ffffff; --portal-surface-soft: #f7fbff; --portal-border: #d7e3f1; --portal-text: #142235; --portal-muted: #5b6b88; --portal-primary: #1f6feb; --portal-primary-dark: #194fb6; --portal-primary-soft: rgba(31, 111, 235, 0.12); --portal-success: #0f766e; --portal-success-soft: #dff7f1; --portal-warning: #8a5a00; --portal-warning-soft: #fff7e6; --portal-shadow: 0 18px 42px rgba(20, 34, 53, 0.08); --portal-radius-xl: 24px; --portal-radius-lg: 18px; --portal-radius-md: 14px; } * { box-sizing: border-box; } body { color: var(--portal-text); } a { color: var(--portal-primary); } a:hover { color: var(--portal-primary-dark); } .portal-panel { background: var(--portal-surface); border: 1px solid var(--portal-border); border-radius: var(--portal-radius-xl); box-shadow: var(--portal-shadow); } .portal-eyebrow { display: inline-flex; padding: 6px 10px; border-radius: 999px; background: var(--portal-success-soft); color: var(--portal-success); font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; } .portal-muted { color: var(--portal-muted); } .portal-button, .portal-button-secondary { display: inline-flex; align-items: center; justify-content: center; min-height: 46px; padding: 0 18px; border-radius: var(--portal-radius-md); text-decoration: none; font-weight: 700; border: none; cursor: pointer; } .portal-button { background: var(--portal-primary); color: #fff; } .portal-button:hover { background: var(--portal-primary-dark); color: #fff; } .portal-button-secondary { background: #edf3ff; color: var(--portal-primary-dark); } .portal-button-secondary:hover { background: #dfeaff; color: var(--portal-primary-dark); } .portal-field input, .portal-field textarea, .portal-field select { width: 100%; padding: 12px; border-radius: 12px; border: 1px solid #cfd7e6; font-size: 14px; background: #fff; color: var(--portal-text); } .portal-field input:focus, .portal-field textarea:focus, .portal-field select:focus { outline: none; border-color: var(--portal-primary); box-shadow: 0 0 0 4px var(--portal-primary-soft); } .portal-helper { color: var(--portal-muted); font-size: 12px; line-height: 1.6; } .portal-notice { padding: 16px 18px; border-radius: 16px; background: var(--portal-warning-soft); border: 1px solid #f4d39b; color: var(--portal-warning); line-height: 1.7; } .portal-status-grid { display: grid; gap: 12px; } .portal-status-item { padding: 14px 16px; border-radius: 16px; background: var(--portal-surface-soft); border: 1px solid var(--portal-border); } .global-nav { position: sticky; top: 0; z-index: 100; background: rgba(14,26,43,.92); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,.08); padding: 12px 20px; } .global-nav-inner { max-width: 1180px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; gap: 16px; } .global-nav-brand { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; } .global-nav-links { display: flex; gap: 20px; align-items: center; } .global-nav-link { color: #d8e4f8; text-decoration: none; font-size: 14px; font-weight: 500; } .global-nav-link:hover { color: #fff; } a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--portal-primary); outline-offset: 2px; } @media (max-width: 768px) { .global-nav { padding: 10px 14px; } .global-nav-brand { font-size: 16px; } .global-nav-links { gap: 12px; } .global-nav-link { font-size: 13px; } } /* ============================================================ 通用状态组件库(2026-06-26 第一阶段 P1-2) 覆盖:loading / skeleton / empty / error / toast / spinner 所有用户端页面可直接
引用。 ============================================================ */ /* —— Loading 容器 —— */ .state-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 36px 18px; text-align: center; color: #5b6b88; background: #f8fbff; border: 1px solid #d7e3f1; border-radius: 16px; } .state-loading__spinner { width: 28px; height: 28px; border: 3px solid #dbe7f5; border-top-color: #1f6feb; border-radius: 50%; animation: state-spin 0.9s linear infinite; } .state-loading__text { font-size: 14px; line-height: 1.6; margin: 0; } @keyframes state-spin { to { transform: rotate(360deg); } } @media (prefers-reduced-motion: reduce) { .state-loading__spinner { animation-duration: 2.5s; } } /* —— Skeleton 占位(首屏数据未到时用)—— */ .state-skeleton { display: block; padding: 18px; background: #f4f7fb; border: 1px solid #e1e8f3; border-radius: 14px; } .state-skeleton__line { display: block; height: 14px; margin: 10px 0; border-radius: 7px; background: linear-gradient(90deg, #e6ecf5 0%, #f3f6fb 50%, #e6ecf5 100%); background-size: 200% 100%; animation: state-shimmer 1.4s ease-in-out infinite; } .state-skeleton__line--short { width: 38%; } .state-skeleton__line--mid { width: 64%; } .state-skeleton__line--long { width: 90%; } @keyframes state-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } @media (prefers-reduced-motion: reduce) { .state-skeleton__line { animation: none; background: #e6ecf5; } } /* —— Empty 空状态 —— */ .state-empty { padding: 28px 20px; border-radius: 16px; background: #f8fbff; border: 1px solid #d7e3f1; color: #5b6b88; text-align: center; } .state-empty__title { margin: 0 0 6px; font-size: 16px; font-weight: 700; color: #2c3e5b; } .state-empty__hint { margin: 0; font-size: 13px; line-height: 1.7; } /* —— Error 错误状态 —— */ .state-error { padding: 20px 18px; border-radius: 14px; background: #fff5f5; border: 1px solid #f5c2c7; color: #b42318; } .state-error__title { margin: 0 0 6px; font-size: 15px; font-weight: 700; } .state-error__hint { margin: 0; font-size: 13px; line-height: 1.7; color: #8a1f15; } .state-error a { color: #b42318; text-decoration: underline; } /* —— Toast(全局,aria-live=polite)—— */ .state-toast-stack { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); display: flex; flex-direction: column; gap: 8px; z-index: 9999; pointer-events: none; } .state-toast { pointer-events: auto; min-width: 220px; max-width: 90vw; padding: 10px 14px; border-radius: 10px; background: rgba(20, 34, 53, .92); color: #fff; font-size: 13px; line-height: 1.5; box-shadow: 0 12px 30px rgba(20, 34, 53, .18); opacity: 0; transform: translateY(8px); transition: opacity .2s ease, transform .2s ease; } .state-toast--visible { opacity: 1; transform: translateY(0); } .state-toast--success { background: #1f7a4d; } .state-toast--error { background: #b42318; } .state-toast--info { background: #1f6feb; } /* —— 工具类:sr-only(可访问性配套)—— */ .state-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }