/* ds-components v1.1 — BEGIN CSS component của DS_. Chỉ dùng biến --ds-* (tokens.css); cấm mã màu/px thô ngoài khối này. Từ vựng lớp = cột "Kiểu trình bày" của UICONV §11 (xem DS §3). Khối này được chép NGUYÊN VĂN vào components.html và mọi HIFI_*.html — kiểm khớp bằng scripts/design-check.mjs. */ *,*::before,*::after{box-sizing:border-box} html{font-family:var(--ds-font-family-base);font-size:var(--ds-font-size-sm);line-height:var(--ds-font-line-height-normal);color:var(--ds-color-text-primary);background:var(--ds-color-bg-canvas);-webkit-font-smoothing:antialiased} body{margin:0} h1,h2,h3{margin:0;line-height:var(--ds-font-line-height-tight);font-weight:var(--ds-font-weight-semibold)} h1{font-size:var(--ds-font-size-xl)}h2{font-size:var(--ds-font-size-lg)}h3{font-size:var(--ds-font-size-md)} a,.ds-link{color:var(--ds-color-brand-primary);text-decoration:underline;text-underline-offset:2px;cursor:pointer} a:hover,.ds-link:hover{color:var(--ds-color-brand-primary-hover)} :focus-visible{outline:2px solid var(--ds-color-focus-ring);outline-offset:2px} .ds-muted{color:var(--ds-color-text-secondary)} .ds-small{font-size:var(--ds-font-size-xs)} .ds-icon{width:var(--ds-size-icon);height:var(--ds-size-icon);flex:none;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round} .ds-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)} /* Layout */ .ds-page{max-width:var(--ds-size-content-max);margin:0 auto;padding:var(--ds-space-6) var(--ds-space-4);display:grid;gap:var(--ds-space-4)} .ds-row{display:flex;gap:var(--ds-space-2);align-items:center;flex-wrap:wrap} .ds-row--between{justify-content:space-between}.ds-row--end{justify-content:flex-end} .ds-stack{display:grid;gap:var(--ds-space-3)} .ds-cols{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--ds-space-4);align-items:start} .ds-header{position:sticky;top:0;z-index:5;background:var(--ds-color-bg-surface);border-bottom:1px solid var(--ds-color-border-default);padding:var(--ds-space-3) var(--ds-space-4);display:flex;align-items:center;gap:var(--ds-space-4);min-height:56px} .ds-breadcrumb{display:flex;gap:var(--ds-space-2);align-items:center;font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)} .ds-breadcrumb a{text-decoration:none} /* Nút — UICONV §11: nút chính / nút phụ / nút nguy hiểm / liên kết */ .ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--ds-space-2);height:var(--ds-size-control-desktop);padding:0 var(--ds-space-4);border-radius:var(--ds-radius-sm);border:1px solid transparent;font:inherit;font-weight:var(--ds-font-weight-semibold);cursor:pointer;white-space:nowrap;text-decoration:none;transition:background .12s} .ds-btn--primary{background:var(--ds-color-brand-primary);color:var(--ds-color-text-inverse)} .ds-btn--primary:hover{background:var(--ds-color-brand-primary-hover)} .ds-btn--secondary{background:var(--ds-color-bg-surface);color:var(--ds-color-text-primary);border-color:var(--ds-color-border-strong)} .ds-btn--secondary:hover{background:var(--ds-color-bg-subtle)} .ds-btn--danger{background:var(--ds-color-status-danger);color:var(--ds-color-text-inverse)} .ds-btn--ghost{background:transparent;color:var(--ds-color-brand-primary)} .ds-btn--icon{width:var(--ds-size-control-desktop);padding:0} .ds-btn[aria-disabled="true"],.ds-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none} .ds-btn.is-loading{pointer-events:none} .ds-spinner{width:16px;height:16px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:ds-spin .8s linear infinite;flex:none} @keyframes ds-spin{to{transform:rotate(360deg)}} /* Field — UICONV §4: nhãn trên field, * sau nhãn, lỗi dưới field thay hint */ .ds-field{display:grid;gap:var(--ds-space-1)} .ds-field__label{font-size:var(--ds-font-size-xs);font-weight:var(--ds-font-weight-semibold);color:var(--ds-color-text-primary)} .ds-field__label .ds-req{color:var(--ds-color-status-danger);margin-left:2px} .ds-field__hint{font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)} .ds-field__error{font-size:var(--ds-font-size-xs);color:var(--ds-color-status-danger);display:flex;gap:var(--ds-space-1);align-items:center} .ds-input,.ds-select,.ds-textarea{width:100%;height:var(--ds-size-control-desktop);padding:0 var(--ds-space-3);border:1px solid var(--ds-color-border-strong);border-radius:var(--ds-radius-sm);background:var(--ds-color-bg-surface);color:var(--ds-color-text-primary);font:inherit} .ds-textarea{height:auto;min-height:88px;padding:var(--ds-space-2) var(--ds-space-3)} .ds-input::placeholder{color:var(--ds-color-text-secondary)} .ds-input:hover,.ds-select:hover{border-color:var(--ds-color-text-secondary)} .ds-input:focus,.ds-select:focus{border-color:var(--ds-color-brand-primary);outline:2px solid var(--ds-color-focus-ring);outline-offset:0} .ds-input.is-invalid,.ds-select.is-invalid{border-color:var(--ds-color-status-danger)} .ds-input[disabled],.ds-select[disabled]{background:var(--ds-color-bg-subtle);color:var(--ds-color-text-disabled);cursor:not-allowed} .ds-input[readonly]{border-color:transparent;background:transparent;padding-left:0} .ds-input-wrap{position:relative} .ds-input-wrap .ds-icon{position:absolute;left:var(--ds-space-3);top:50%;transform:translateY(-50%);color:var(--ds-color-text-secondary)} .ds-input-wrap .ds-input{padding-left:calc(var(--ds-space-3) + var(--ds-size-icon) + var(--ds-space-2))} .ds-select{appearance:none;background-image:url("data:image/svg+xml;utf8,");background-repeat:no-repeat;background-position:right var(--ds-space-3) center;padding-right:calc(var(--ds-space-3) + 24px)} .ds-check{display:inline-flex;align-items:center;gap:var(--ds-space-2);cursor:pointer;min-height:var(--ds-size-control-desktop)} .ds-check input{width:18px;height:18px;margin:0;accent-color:var(--ds-color-brand-primary)} .ds-switch{position:relative;width:40px;height:22px;border-radius:var(--ds-radius-pill);background:var(--ds-color-border-strong);display:inline-block;flex:none} .ds-switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--ds-color-bg-surface)} .ds-switch[aria-checked="true"]{background:var(--ds-color-brand-primary)} .ds-switch[aria-checked="true"]::after{left:20px} .ds-stepper{display:inline-flex;border:1px solid var(--ds-color-border-strong);border-radius:var(--ds-radius-sm);height:var(--ds-size-control-desktop);overflow:hidden} .ds-stepper button{width:var(--ds-size-control-desktop);border:0;background:var(--ds-color-bg-surface);font:inherit;cursor:pointer;color:var(--ds-color-text-primary)} .ds-stepper input{width:48px;text-align:center;border:0;border-left:1px solid var(--ds-color-border-default);border-right:1px solid var(--ds-color-border-default);font:inherit} /* Bảng / Card — UICONV §3, §11 */ .ds-table-wrap{overflow-x:auto;border:1px solid var(--ds-color-border-default);border-radius:var(--ds-radius-md);background:var(--ds-color-bg-surface)} .ds-table{width:100%;border-collapse:collapse;font-size:var(--ds-font-size-sm)} .ds-table th,.ds-table td{padding:var(--ds-space-3) var(--ds-space-4);text-align:left;border-bottom:1px solid var(--ds-color-border-default);vertical-align:middle} .ds-table th{background:var(--ds-color-bg-subtle);font-weight:var(--ds-font-weight-semibold);font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary);white-space:nowrap} .ds-table th[aria-sort]{cursor:pointer} .ds-table tbody tr:hover{background:var(--ds-color-bg-canvas)} .ds-table tbody tr[aria-selected="true"]{background:var(--ds-color-brand-primary-subtle)} .ds-table tbody tr:last-child td{border-bottom:0} .ds-table td.ds-num{text-align:right;font-variant-numeric:tabular-nums} .ds-card{background:var(--ds-color-bg-surface);border:1px solid var(--ds-color-border-default);border-radius:var(--ds-radius-md);padding:var(--ds-space-4);display:grid;gap:var(--ds-space-2)} .ds-cards{display:grid;gap:var(--ds-space-3)} .ds-badge{display:inline-flex;align-items:center;height:22px;padding:0 var(--ds-space-2);border-radius:var(--ds-radius-pill);font-size:var(--ds-font-size-xs);font-weight:var(--ds-font-weight-semibold);background:var(--ds-color-bg-subtle);color:var(--ds-color-text-primary)} .ds-badge--info{background:var(--ds-color-brand-primary-subtle)} .ds-badge--success{background:var(--ds-color-status-success-subtle);color:var(--ds-color-status-success)} .ds-badge--warning{background:var(--ds-color-status-warning-subtle);color:var(--ds-color-status-warning)} .ds-badge--danger{background:var(--ds-color-status-danger-subtle);color:var(--ds-color-status-danger)} .ds-tabs{display:flex;gap:var(--ds-space-4);border-bottom:1px solid var(--ds-color-border-default)} .ds-tab{padding:var(--ds-space-2) 0;border-bottom:2px solid transparent;color:var(--ds-color-text-secondary);cursor:pointer;font-weight:var(--ds-font-weight-semibold)} .ds-tab[aria-selected="true"]{color:var(--ds-color-brand-primary);border-bottom-color:var(--ds-color-brand-primary)} .ds-pagination{display:flex;gap:var(--ds-space-2);align-items:center;font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)} .ds-price{font-variant-numeric:tabular-nums;font-weight:var(--ds-font-weight-semibold)} .ds-thumb{width:64px;height:64px;border-radius:var(--ds-radius-sm);background:var(--ds-color-bg-subtle);border:1px solid var(--ds-color-border-default);display:grid;place-items:center;color:var(--ds-color-text-secondary);flex:none} /* Phản hồi hệ thống — UICONV §5, §6 */ .ds-toast{position:fixed;top:var(--ds-space-4);right:var(--ds-space-4);z-index:20;min-width:280px;max-width:420px;padding:var(--ds-space-3) var(--ds-space-4);border-radius:var(--ds-radius-md);background:var(--ds-color-text-primary);color:var(--ds-color-text-inverse);box-shadow:var(--ds-shadow-md);display:flex;gap:var(--ds-space-3);align-items:flex-start} .ds-toast--error{background:var(--ds-color-status-danger)} .ds-banner{display:flex;gap:var(--ds-space-3);align-items:flex-start;padding:var(--ds-space-3) var(--ds-space-4);border-radius:var(--ds-radius-md);border:1px solid var(--ds-color-border-default);background:var(--ds-color-bg-subtle)} .ds-banner--error{background:var(--ds-color-status-danger-subtle);border-color:var(--ds-color-status-danger);color:var(--ds-color-status-danger)} .ds-banner--warning{background:var(--ds-color-status-warning-subtle);border-color:var(--ds-color-status-warning)} .ds-empty{display:grid;justify-items:center;text-align:center;gap:var(--ds-space-3);padding:var(--ds-space-12) var(--ds-space-4);color:var(--ds-color-text-secondary)} .ds-empty__illus{width:120px;height:88px;color:var(--ds-color-border-strong)} .ds-empty--filter{padding:var(--ds-space-8) var(--ds-space-4)} .ds-skeleton{height:14px;border-radius:var(--ds-radius-sm);background:linear-gradient(90deg,var(--ds-color-bg-subtle),var(--ds-color-bg-canvas),var(--ds-color-bg-subtle));background-size:200% 100%;animation:ds-shimmer 1.2s infinite} @keyframes ds-shimmer{to{background-position:-200% 0}} .ds-modal-backdrop{position:fixed;inset:0;background:#1B1F2480;display:grid;place-items:center;z-index:10;padding:var(--ds-space-4)} .ds-modal{width:100%;max-width:520px;background:var(--ds-color-bg-surface);border-radius:var(--ds-radius-md);box-shadow:var(--ds-shadow-md);display:grid} .ds-modal__head{display:flex;justify-content:space-between;align-items:center;padding:var(--ds-space-4) var(--ds-space-6);border-bottom:1px solid var(--ds-color-border-default)} .ds-modal__body{padding:var(--ds-space-6);display:grid;gap:var(--ds-space-4)} .ds-modal__foot{display:flex;justify-content:flex-end;gap:var(--ds-space-2);padding:var(--ds-space-4) var(--ds-space-6);border-top:1px solid var(--ds-color-border-default)} .ds-status-page{display:grid;justify-items:center;gap:var(--ds-space-3);padding:var(--ds-space-12);text-align:center} /* Trạng thái ẩn/khoá/read-only — W7 */ [data-vis="hidden"]{display:none!important} [data-vis="disabled"]{opacity:.45;pointer-events:none} [data-vis="readonly"] .ds-input{border-color:transparent;background:transparent} /* Responsive — breakpoint theo UICONV §1; body[data-view] ép khung nhìn khi xem trong HIFI */ @media (max-width:1023px){.ds-cols{grid-template-columns:1fr}} @media (max-width:767px){ .ds-page{padding:var(--ds-space-4) var(--ds-space-3)} .ds-btn{height:var(--ds-size-control-touch)} .ds-input,.ds-select{height:var(--ds-size-control-touch)} .ds-table-wrap{display:none}.ds-cards{display:grid} .ds-hide-m{display:none!important} } @media (min-width:768px){.ds-cards{display:none}.ds-hide-d{display:none!important}} body[data-view="mobile"] .ds-cols{grid-template-columns:1fr} body[data-view="mobile"] .ds-table-wrap{display:none}body[data-view="mobile"] .ds-cards{display:grid} body[data-view="mobile"] .ds-hide-m{display:none!important}body[data-view="mobile"] .ds-hide-d{display:revert} body[data-view="mobile"] .ds-btn,body[data-view="mobile"] .ds-input,body[data-view="mobile"] .ds-select{height:var(--ds-size-control-touch)} body[data-view="mobile"] .ds-page{padding:var(--ds-space-4) var(--ds-space-3)} body[data-view="tablet"] .ds-cols{grid-template-columns:1fr} /* Tiện ích xếp chồng ở mobile: phần tử trong .ds-row chiếm trọn hàng (nhóm nút thao tác của một dòng danh sách/CartItem…) */ .ds-m-full{} @media (max-width:767px){.ds-m-full{flex-basis:100%;min-width:0}} body[data-view="mobile"] .ds-m-full{flex-basis:100%;min-width:0} /* ds-components v1.1 — END */