

:root {
  
  --qf-primary:        #2B5CFF;   
  --qf-primary-hover:  #1E50E8;   
  --qf-primary-text:   #1E50E8;   /* 文字专用：#2B5CFF 压在半透明品牌底上只有 4.35:1 */
  --qf-primary-soft:   rgba(43, 92, 255, 0.12);
  --qf-primary-ring:   rgba(43, 92, 255, 0.35);
  --qf-accent:         #00C2FF;   

  
  --qf-success: #0E9F5B;
  --qf-success-text: #0A7A46;  /* 状态文字：#0E9F5B 压在同色软底上只有 3.0:1 */
  --qf-on-brand: #FFFFFF;       /* 品牌/状态实心徽标上的文字 */
  --qf-warning: #B26A00;
  --qf-danger:  #D92D20;
  --qf-info:    #2B5CFF;

  
  /*
   * 结构色：已收敛。
   * 这 6 个值 = 收敛前浏览器里【实际渲染】的值（原先由 v4-skin.css 的字面重赋胜出）。
   * 收进这里是为了让 tokens.css 成为唯一真源：改动这里才会真的改变渲染。
   * 校验依据：_audit/ui_refactor/baseline/theme_colors.json（改前 11 站实测）。
   *
   * 注意 --qf-card 是独立令牌，不能和 --qf-surface 合一：
   * 亮色下两者同为 #FFFFFF，但暗色下卡片(#131a2a)比控件浮起面(#1A2029)更暗，
   * 合并会让 ghost 按钮和卡片同色、在卡片上失去边界（实测发现的回归）。
   */
  --qf-bg:            #fbfcfe;
  --qf-surface:       #FFFFFF;
  --qf-surface-hover: #F0F2F7;
  --qf-card:          #FFFFFF;
  --qf-border:        #e4e8f0;
  --qf-border-soft:   #edf0f6;
  --qf-text:          #0f1729;
  --qf-text-muted:    #5b6478;

  /*
   * 滚动条。此前 4 套色各自拿「边框色」充当滑块，22 组站点×主题共 44 项实测全部低于 3:1
   * （亮色 1.16–1.47、暗色 1.38–1.91），视觉上等于没有滚动条。
   * WCAG 2.2 SC 1.4.11 要求非文本 UI 组件与相邻色 ≥3:1，滑块正是识别组件所必需的视觉信息。
   * 取值在既有色阶上从 --qf-border 向 --qf-text-muted 线性扫 5% 步长，取对最不利背景
   * （亮 #F0F2F7 / 暗 #222A35）仍达标的最浅一档：亮 #7d8596（3.31）、暗 #6c788f（3.27）。
   * track 保持 transparent，让元素自身背景透出，明暗 / 卡片 / 浮起面三种底都自动正确。
   */
  --qf-scrollbar-thumb:       #7d8596;
  --qf-scrollbar-thumb-hover: #70788a;
  --qf-scrollbar-track:       transparent;

  
  --qf-space-1: 4px;  --qf-space-2: 8px;  --qf-space-3: 12px;
  --qf-space-4: 16px; --qf-space-5: 24px; --qf-space-6: 32px;
  --qf-radius-sm: 8px; --qf-radius-md: 12px; --qf-radius-lg: 16px;
  --qf-shadow-sm: 0 1px 2px rgba(16,24,40,.06), 0 1px 3px rgba(16,24,40,.08);
  --qf-shadow-md: var(--shadow-md);
  --qf-shadow-lg: 0 18px 44px rgba(16,24,40,.16), 0 4px 12px rgba(16,24,40,.08);

  
  --bp-sm: 640px;   
  --bp-md: 768px;   
  --bp-lg: 1024px;  
  --bp-xl: 1280px;  

  
  /*
   * 实底渐变会被当成按钮/徽标的文字背景（白字），两端的亮度都必须撑得住白字：
   * 原先第二档 #00C2FF 是纯青，白字实测只有 1.9:1；#0B76B4 把青压成钢蓝，保留
   * 蓝→青的走向，同时让两端都不低于 4.9:1。装饰性青色仍由 --brand-2 承担。
   */
  --qf-brand-grad: linear-gradient(135deg, #2B5CFF 0%, #0B76B4 100%);
  --qf-brand-grad-soft: linear-gradient(135deg, rgba(43,92,255,.12) 0%, rgba(0,194,255,.12) 100%);
  --qf-brand-glow: rgba(43, 92, 255, .28);

  
  --qf-success-soft: rgba(14, 159, 91, .12);
  --qf-warning-soft: rgba(178, 106, 0, .13);
  --qf-danger-soft:  rgba(217, 45, 32, .12);
  --qf-info-soft:    rgba(43, 92, 255, .10);

  
  --qf-info-note:        rgba(43, 92, 255, .07);
  --qf-info-note-ic:     var(--qf-primary);
  --qf-info-note-title:  #1E50E8;
  --qf-info-note-text:   var(--qf-text);

  --qf-success-note:      rgba(14, 159, 91, .08);
  --qf-success-note-ic:   var(--qf-success);
  --qf-success-note-title:#046C4E;
  --qf-success-note-text: var(--qf-text);

  --qf-warning-note:      rgba(178, 106, 0, .09);
  --qf-warning-note-ic:   var(--qf-warning);
  --qf-warning-note-title:#8A4B00;
  --qf-warning-note-text: var(--qf-text);

  --qf-danger-note:       rgba(217, 45, 32, .07);
  --qf-danger-note-ic:    var(--qf-danger);
  --qf-danger-note-title: #A81E14;
  --qf-danger-note-text:  var(--qf-text);

  
  --qf-note-neutral:      rgba(16, 24, 40, .035);

  
  --qf-code-bg:   #0D1B24;
  --qf-code-text: #C7E4F5;   
  --qf-code-border: rgba(37, 99, 235, .28);

  --qf-focus-ring: 0 0 0 3px var(--qf-primary-ring);

  
  --qf-z-base: 0;
  --qf-z-raised: 10;
  --qf-z-sticky: 100;
  --qf-z-header: 200;
  --qf-z-dropdown: 400;
  --qf-z-overlay: 800;
  --qf-z-modal: 900;
  --qf-z-toast: 1000;
  --qf-z-tooltip: 1100;

  /*
   * 以下令牌从 brand-tokens.css 并入。
   * 为什么并：那个文件只被 3 个站的 5 个页面引用，同一站内首页有、内页没有，
   * 令牌集合不一致 —— 内页只能吃各处 fallback。tokens.css 是全部站点全部页面都加载的文件，
   * 并入后集合天然一致，也免去给 11 个页面各补一处 <link>。
   * 零视觉变更的依据：54 个令牌中 51 个全站（16MB css/html/js）零引用；
   * --qf-ease-standard / --qf-ease-out 的 84 处引用 fallback 与本值完全相同；
   * --qf-font-mono 只被 polish-layer 的 --mono 引用，而 --mono 全站零使用。
   */

  /* 语义色的描边 / 极淡底 / 反白文字 */
  --qf-primary-border:  rgba(43, 92, 255, 0.42);
  --qf-on-primary:      #ffffff;
  --qf-success-border:  rgba(14, 159, 91, .38);
  --qf-success-faint:   rgba(14, 159, 91, .07);
  --qf-on-success:      #ffffff;
  --qf-warning-border:  rgba(178, 106, 0, .38);
  --qf-warning-faint:   rgba(178, 106, 0, .07);
  --qf-on-warning:      #ffffff;
  --qf-danger-border:   rgba(217, 45, 32, .38);
  --qf-danger-faint:    rgba(217, 45, 32, .07);
  --qf-on-danger:       #ffffff;
  --qf-info-border:     rgba(43, 92, 255, .38);
  --qf-info-faint:      rgba(43, 92, 255, .07);
  --qf-on-info:         #ffffff;

  /* 扩展面 */
  --qf-bg-elevated:     #FFFFFF;
  --qf-surface-active:  #E7EBF3;
  --qf-text-inverse:    #FFFFFF;
  --qf-border-strong:   #C6CFDD;

  /* 字体族 */
  --qf-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
                  "Hiragino Sans GB", "Microsoft YaHei", system-ui, sans-serif;
  --qf-font-mono: "SF Mono", "JetBrains Mono", "Cascadia Mono",
                  "Roboto Mono", Menlo, Consolas, monospace;
  --qf-font-serif: "Iowan Old Style", "Palatino Linotype", Palatino,
                   "Book Antiqua", Georgia, "Songti SC", "Source Han Serif SC", serif;

  /*
   * 字阶 —— 8 档。
   * 收敛依据：全站原有 7167 处 font-size、119 种写法，单页最多 28 种字号（www@390），
   * 其中 17 种是 clamp / vw 在窄屏算出的 12.48px 这类小数。收敛后只允许这 8 个值，
   * 由 _tools/apply_type_scale.js 机械吸附、_tools/check_type_scale.js 在真浏览器复核。
   * 12px 是硬地板：中文 12px 以下笔画粘连，属可用性问题（老花、低 DPI、放大镜用户），
   * 目标写死 0 处而不是「减少」。
   * xs→5xl 的序列与 sm/md、xl/lg、5xl 的同值别名是为兼容旧 var 引用保留的，不代表多出档位。
   */
  /*
   * V8-P3：阶梯与桌面端对齐（12 / 12 / 13 / 14 / 16 / 18 / 22 / 28 / 36）。
   * 改前是 12/12/13/14/16/16/18/22/28，比桌面整条低一档 —— xl 曾是 lg 的同义别名
   * （都 16）、2xl 18 对桌面 22、3xl 22 对 28、4xl 28 对 36；只有顶端 5xl=36 碰巧相等，
   * 等于「顶端对齐、中间错位」，最难发现的一种错位。
   *
   * 收编后本阶梯与 theme.css 的桌面端**前九档完全一致**，5xl 删除：它在全站
   * 0 处引用（真正的最大档现在叫 4xl=36，与桌面同名同值），留着就是一个
   * 「名字叫 5xl、值和 4xl 一样」的陷阱。
   *
   * ⚠️ 已知边界：这 10 个令牌在本仓站点 CSS 里的引用数是 0 —— 站点实际排版用的是
   * 541 条 font-size 字面量。所以本次对齐是**令牌层的规格统一**，不产生任何渲染变化；
   * 真正让网站吃到这套阶梯，需要把字面量逐条收编，属网站侧排版重做，未在本轮范围。
   */
  /* qf-design:font-size begin */
  --qf-font-size-xs:   12px;   /* 1 · 元信息 / 最小正文 */
  --qf-font-size-sm:   12px;   /* 同义别名 → xs */
  --qf-font-size-md:   13px;   /* 2 · 次要正文 */
  --qf-font-size-base: 14px;   /* 3 · 正文 */
  --qf-font-size-lg:   16px;   /* 4 · 强调正文 / lead */
  --qf-font-size-xl:   18px;   /* 5 · 小标题 */
  --qf-font-size-2xl:  22px;   /* 6 · h4 */
  --qf-font-size-3xl:  28px;   /* 7 · h3 */
  --qf-font-size-4xl:  36px;   /* 8 · h1 / display（最大档） */
  /* qf-design:font-size end */

  --qf-line-height-tight: 1.3;
  --qf-line-height-snug:  1.5;
  --qf-line-height-base:  1.6;
  --qf-line-height-loose: 1.8;

  --qf-font-weight-normal:   400;
  --qf-font-weight-medium:   500;
  --qf-font-weight-semibold: 600;
  --qf-font-weight-bold:     700;

  /* 间距 / 圆角补位 */
  --qf-space-7:     48px;
  --qf-space-8:     64px;
  --qf-radius-full: 999px;

  /* 动效 */
  --qf-duration-fast: 120ms;
  --qf-duration-base: 200ms;
  --qf-duration-slow: 320ms;
  --qf-ease-standard: cubic-bezier(.4, 0, .2, 1);
  --qf-ease-out:      cubic-bezier(0, 0, .2, 1);

  /* 断点（供 JS 读取；CSS 媒体查询无法用 var，这里只是单一真源） */
  --qf-bp-sm: 640px;
  --qf-bp-md: 768px;
  --qf-bp-lg: 1024px;
  --qf-bp-xl: 1280px;

  /* 交互尺寸与焦点 */
  --qf-touch-target: 44px;
  --qf-focus-outline: 2px solid var(--qf-primary);
  --qf-focus-offset: 2px;

  /*
   * V7 设计语言（2026-10-30）。只放「颜色 / 阴影 / 渐变」——
   * 长度与字号由 polish-layer §36 直接写，避免为一次排版再增一层令牌。
   * 这里出现的每个值都会被 11 站共用，改这里 = 全站改版；组件规则在 polish-layer.css。
   */
  --qf-v7-hero-bg:
    radial-gradient(1100px 520px at 50% -12%, rgba(43, 92, 255, .16) 0%, rgba(43, 92, 255, 0) 68%),
    linear-gradient(180deg, #F5F8FF 0%, #FBFCFE 100%);
  --qf-v7-tint: #F5F7FD;
  --qf-v7-hairline: rgba(43, 92, 255, .18);
  --qf-v7-divider: linear-gradient(90deg,
      rgba(43, 92, 255, 0) 0%, rgba(43, 92, 255, .30) 50%, rgba(43, 92, 255, 0) 100%);
  --qf-v7-eyebrow-bg: rgba(43, 92, 255, .10);
  --qf-v7-eyebrow-bd: rgba(43, 92, 255, .24);
  --qf-v7-eyebrow-fg: #1E50E8;
  --qf-v7-card-shadow:
    0 1px 2px rgba(10, 20, 48, .05),
    0 16px 36px -22px rgba(10, 20, 48, .22);
  --qf-v7-card-shadow-hover:
    0 2px 8px rgba(10, 20, 48, .07),
    0 30px 60px -26px rgba(10, 20, 48, .34);
  --qf-v7-btn-shadow: 0 10px 26px -14px rgba(43, 92, 255, .62);
  --qf-v7-btn-shadow-hover: 0 16px 34px -14px rgba(43, 92, 255, .70);

  /*
   * 墨色层（V7 的核心：官网首屏与全站页脚改用深墨底）。
   * 亮暗两套共用同一批值 —— 深色块本身就是「另一种主题」，不随页面主题翻转，
   * 否则深色站里会出现「深底 + 深底」。上面的 on-ink 系列全部按最暗底 #0A1226 校验过：
   * #F3F7FF 15.6:1 / #C3D0EA 10.2:1 / #9FB0D0 7.1:1 / #9FC0FF 10.5:1。
   */
  --qf-v7-ink: #0A1226;
  --qf-v7-ink-grad: linear-gradient(162deg, #0A1226 0%, #0E1A34 52%, #0A1428 100%);
  --qf-v7-ink-hair: rgba(255, 255, 255, .14);
  --qf-v7-ink-glass: rgba(255, 255, 255, .07);
  --qf-v7-ink-glass-line: rgba(255, 255, 255, .18);
  --qf-v7-on-ink: #F3F7FF;
  --qf-v7-on-ink-2: #C3D0EA;
  --qf-v7-on-ink-muted: #9FB0D0;
  --qf-v7-on-ink-brand: #9FC0FF;
  --qf-v7-glow-ink: radial-gradient(ellipse at 30% 40%, rgba(43, 92, 255, .38) 0%, rgba(43, 92, 255, 0) 58%),
    radial-gradient(ellipse at 74% 26%, rgba(0, 194, 255, .26) 0%, rgba(0, 194, 255, 0) 62%);
  --qf-v7-grad-text-ink: linear-gradient(135deg, #8FB2FF 0%, #6FE3FF 100%);
  /* 墨色底上的产品窗口投影：深底需要更重的落影才有浮起感 */
  --qf-v7-ink-window-shadow: 0 42px 96px -44px rgba(0, 0, 0, .85), 0 2px 10px -4px rgba(0, 0, 0, .55);

  /*
   * 原生控件配色：原先只在 brand-tokens.css 里声明，导致 8 站缺失。
   * 注意 theme-boot.js / site.js 会写内联 style.colorScheme（优先级更高），
   * 这里保留声明只为「JS 未执行」时仍有正确兜底。
   */
  color-scheme: light;
}


[data-theme="dark"] {
  --qf-primary:        #6C8FFF;          
  --qf-primary-hover:  #9FB5FF;          
  --qf-primary-text:   #93B0FF;          /* 暗色下压在品牌软底上的最浅可用一档 */
  --qf-primary-soft:   rgba(108, 143, 255, .16);
  --qf-primary-ring:   rgba(108, 143, 255, .42);
  --qf-accent:         #4FD8FF;          

  --qf-success: #34D399;
  --qf-success-text: #34D399;
  --qf-on-brand: #0B1220;       /* 暗色下品牌色变浅，徽标文字必须转深 */
  --qf-warning: #FBBF24;
  --qf-danger:  #F87171;
  --qf-info:    #6C8FFF;

  /*
   * 结构色（暗色）：同亮色，取收敛前实际渲染值。
   * --qf-surface 保持 #1A2029（控件浮起面，ghost 按钮等用它）；
   * --qf-card 单独为 #131a2a（卡片面，比浮起面更暗，两者不可互换）。
   */
  --qf-bg:            #070a12;
  --qf-surface:       #1A2029;
  --qf-surface-hover: #222A35;
  --qf-card:          #131a2a;
  --qf-border:        #232d43;
  --qf-border-soft:   #1c2438;
  --qf-text:          #e7ecf7;
  --qf-text-muted:    #93A1B8;

  /*
   * 滚动条（暗色）—— 取值方法与亮色同（见亮色块注释），但约束方向相反：
   * 暗底上要找"最暗的可读档"，且必须按最亮的那层暗底校验。
   * 最不利背景 = --qf-surface-hover #222A35（比卡片 #131a2a、浮起面 #1A2029 都亮），
   * 所以不能只对 --qf-bg 算：那样会得出 #5b677e（对 bg 3.47 看着过），
   * 实际放进浮起面板里只有 2.54:1 —— 这类"只对页面底校验"的错误最容易漏。
   * 常态 #6c788f 对四种暗底最差 3.27；hover #77849b 最差 3.83。
   */
  --qf-scrollbar-thumb:       #6c788f;
  --qf-scrollbar-thumb-hover: #77849b;
  --qf-scrollbar-track:       transparent;

  --qf-shadow-sm: 0 1px 2px rgba(0,0,0,.40);
  --qf-shadow-md: var(--shadow-md);
  --qf-shadow-lg: 0 18px 44px rgba(0,0,0,.55);

  --qf-brand-grad:      linear-gradient(135deg, #6C8FFF 0%, #4FD8FF 100%);
  --qf-brand-grad-soft: linear-gradient(135deg, rgba(108,143,255,.16) 0%, rgba(79,216,255,.16) 100%);
  --qf-brand-glow:      rgba(108, 143, 255, .32);

  --qf-success-soft: rgba(52, 211, 153, .16);
  --qf-warning-soft: rgba(251, 191, 36, .16);
  --qf-danger-soft:  rgba(248, 113, 113, .16);
  --qf-info-soft:    rgba(108, 143, 255, .16);

  
  --qf-info-note:        rgba(108, 143, 255, .10);
  --qf-info-note-ic:     #9FB5FF;
  --qf-info-note-title:  #D2DDFF;
  --qf-info-note-text:   var(--qf-text);

  --qf-success-note:      rgba(52, 211, 153, .10);
  --qf-success-note-ic:   #6EE7B7;
  
  --qf-success-note-title:#86EFAC;
  --qf-success-note-text: var(--qf-text);

  --qf-warning-note:      rgba(251, 191, 36, .09);
  --qf-warning-note-ic:   #FCD34D;
  --qf-warning-note-title:#FDE68A;
  --qf-warning-note-text: var(--qf-text);

  --qf-danger-note:       rgba(248, 113, 113, .09);
  --qf-danger-note-ic:    #FCA5A5;
  --qf-danger-note-title: #FECACA;
  --qf-danger-note-text:  var(--qf-text);

  --qf-note-neutral:      rgba(255, 255, 255, .045);

  /* ═══ 从 brand-tokens.css 并入（暗色覆盖），同亮色段说明 ═══ */
  --qf-primary-border:  rgba(108, 143, 255, .52);
  --qf-on-primary:      #0B1220;
  --qf-success-border:  rgba(52, 211, 153, .38);
  --qf-success-faint:   rgba(52, 211, 153, .08);
  --qf-on-success:      #0B1220;
  --qf-warning-border:  rgba(251, 191, 36, .38);
  --qf-warning-faint:   rgba(251, 191, 36, .08);
  --qf-on-danger:       #0B1220;

  --qf-bg-elevated:     #111A2B;
  --qf-surface-active:  #243052;
  --qf-text-inverse:    #0B1220;
  --qf-border-strong:   rgba(233, 237, 246, .24);

  --qf-focus-ring: 0 0 0 3px var(--qf-primary-ring);

  /* V7 设计语言（暗色）：与亮色同结构，亮度方向相反 —— 底更深、光更强、描边更亮 */
  --qf-v7-hero-bg:
    radial-gradient(1000px 520px at 50% -14%, rgba(108, 143, 255, .22) 0%, rgba(108, 143, 255, 0) 68%),
    linear-gradient(180deg, #0A1020 0%, #070a12 100%);
  --qf-v7-tint: #0C1220;
  --qf-v7-hairline: rgba(108, 143, 255, .30);
  --qf-v7-divider: linear-gradient(90deg,
      rgba(108, 143, 255, 0) 0%, rgba(108, 143, 255, .42) 50%, rgba(108, 143, 255, 0) 100%);
  --qf-v7-eyebrow-bg: rgba(108, 143, 255, .16);
  --qf-v7-eyebrow-bd: rgba(108, 143, 255, .34);
  --qf-v7-eyebrow-fg: #93B0FF;
  --qf-v7-card-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 18px 40px -24px rgba(0, 0, 0, .75);
  --qf-v7-card-shadow-hover: 0 2px 8px rgba(0, 0, 0, .50), 0 32px 64px -26px rgba(0, 0, 0, .85);
  --qf-v7-btn-shadow: 0 10px 26px -14px rgba(108, 143, 255, .50);
  --qf-v7-btn-shadow-hover: 0 16px 34px -14px rgba(108, 143, 255, .58);

  color-scheme: dark;
}




.qf-skeleton {
  display: block;
  border-radius: var(--qf-radius-sm);
  background:
    linear-gradient(90deg, transparent 0%, var(--qf-surface-hover) 50%, transparent 100%)
      var(--qf-surface-hover);
  background-size: 220% 100%;
  animation: qf-skeleton-sweep 1.35s ease-in-out infinite;
  min-height: 1em;
}
@keyframes qf-skeleton-sweep {
  0%   { background-position: 140% 0; }
  100% { background-position: -40% 0; }
}
.qf-skeleton--title { height: 20px; width: 62%; margin-bottom: 10px; }
.qf-skeleton--line  { height: 13px; margin-bottom: 8px; }
.qf-skeleton--card  { height: 96px; border: 1px solid var(--qf-border); border-radius: var(--qf-radius-md); }
.qf-skeleton-stack   { display: grid; gap: 12px; }


.qf-spinner {
  display: inline-block; width: 18px; height: 18px;
  border: 2px solid var(--qf-primary-soft);
  border-top-color: var(--qf-primary);
  border-radius: 50%;
  animation: qf-spin .7s linear infinite;
  vertical-align: -3px;
}
@keyframes qf-spin { to { transform: rotate(360deg); } }

.qf-loading {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: var(--qf-space-5);
  color: var(--qf-text-muted); font-size: 14px;
}


.qf-empty, .qf-error {
  padding: var(--qf-space-5) var(--qf-space-4);
  text-align: center;
  border: 1px dashed var(--qf-border);
  border-radius: var(--qf-radius-md);
  background: var(--qf-surface);
}
.qf-empty-title, .qf-error-title {
  font-weight: 700; color: var(--qf-text);
  font-size: 16px; margin-bottom: 6px;
}
.qf-empty-body, .qf-error-body {
  font-size: 14px; color: var(--qf-text-muted);
  line-height: 1.7; margin-bottom: var(--qf-space-3);
}
.qf-empty-hint { font-size: 13px; color: var(--qf-text-muted); }


.qf-error {
  border-style: solid;
  border-color: var(--qf-danger-soft);
  background: var(--qf-danger-soft);
}
.qf-error-title { color: var(--qf-danger); }
.qf-error-title::before {
  content: "!"; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-right: 7px; border-radius: 50%;
  background: var(--qf-danger); color: var(--qf-surface);
  font-size: 12px; font-weight: 800; vertical-align: -3px;
}

@media (prefers-reduced-motion: reduce) {
  .qf-skeleton, .qf-spinner { animation: none; }
}


.qf-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 10px 22px;
  border-radius: var(--qf-radius-md);
  font-size: 14px; font-weight: 600; line-height: 1.4;
  border: 1px solid transparent; cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease,
              background .2s ease, color .2s ease, border-color .2s ease;
}
.qf-btn:active { transform: translateY(1px); }
.qf-btn--primary {
  background: var(--qf-brand-grad); color: #fff;
  box-shadow: var(--shadow-brand);
}
.qf-btn--primary:hover { color: #fff; transform: translateY(-1px); }
.qf-btn--ghost {
  background: var(--qf-surface); color: var(--qf-text);
  border-color: var(--qf-border);
}
.qf-btn--ghost:hover { border-color: var(--qf-primary); color: var(--qf-primary); transform: translateY(-1px); }
.qf-btn--danger { background: var(--qf-danger); color: #fff; }
.qf-btn--danger:hover { color: #fff; transform: translateY(-1px); }
.qf-btn[disabled], .qf-btn.disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }


.qf-field-error {
  display: block; margin-top: 5px;
  font-size: 13px; font-weight: 600; color: var(--qf-danger);
}
.qf-field-error:empty { display: none; }
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--qf-danger) !important;
  box-shadow: var(--shadow-sm);
}


:focus-visible {
  outline: 2px solid var(--qf-primary);
  outline-offset: 2px;
}


@media (max-width: 767px) {
  .btn, .btn-sm, .btn-lg, .qf-btn, .qf-btn--lg { min-height: 44px; }
  .icon-btn, .social-btn, .qf-icon-button { min-width: 44px; min-height: 44px; }
  .lang-switch { min-height: 44px; }
  .nav-links a { min-height: 44px; display: flex; align-items: center; }
  .footer-links a { min-height: 44px; display: flex; align-items: center; }
}
