feat(sw): viewer mode header, dropdown and popup panel

- SWViewerHeader: data-driven viewer menu (button / toggle / menu / tabs / switch / custom), More by
  collapseBelow, trailing slot, onCommand; SWHeader logo props with no default logo
- SWViewerMenu: dropdown, check / radio, multi-level submenu, context menu at a point
- SWViewerPanel: viewer popup frame with free children / footer, drag, corner resize, collapse, modal
- Panel controls: SWViewerSection / Field / Hint / Switch / Slider / Input / Button / Badge / Status
- SWHeader black text outline tokens on viewer text, matching icon outline (drop-shadow)
- Storybook SW/Viewer stories; remove SW/Tool story (SW/Popup kept)
- AI guide ai/VIEWER.md, example H, verification scripts and evidence
This commit is contained in:
shkim4 committed 2026-10-19 16:09:26 +09:00
1 parent e8409eff44
commit 7f42923ccf
50 files changed
+3838 -92

No files matched your search

+549
View File
@@ -0,0 +1,549 @@
/* @hm-design/sw — SW viewer mode (reference: DWG/DXF viewer top menu and floating panels, measured 2026-10-19).
Viewer-only tokens. The three header surfaces reuse the user-confirmed SW tokens of sw.css
(--hm-sw-header-document-* / object-* / property-*). Never touches global selectors or the SWHeader. */
.hm-sw-header,
.hm-sw-theme {
/* Header */
--hm-sw-viewer-height: 52px;
--hm-sw-viewer-background: linear-gradient(180deg, #266651 0%, #123328 45%, #0a221d 100%);
--hm-sw-viewer-border-color: #071e18;
--hm-sw-viewer-brand-width: 72px;
/* No logo: no text or icon is drawn; the cap keeps only its slanted edge (no wide empty space). */
--hm-sw-viewer-brand-empty-width: 24px;
--hm-sw-viewer-brand-top: #123328;
--hm-sw-viewer-brand-middle: #205241;
--hm-sw-viewer-brand-bottom: #071e18;
--hm-sw-viewer-text: #fff;
--hm-sw-viewer-muted: #bddfd4;
--hm-sw-viewer-accent: #ffc600;
--hm-sw-viewer-hover: #266651;
--hm-sw-viewer-gap: 4px;
--hm-sw-viewer-radius: 4px;
--hm-sw-viewer-control-height: 40px;
--hm-sw-viewer-button-height: 34px;
--hm-sw-viewer-font-size: 14px;
--hm-sw-viewer-label-size: 11px;
--hm-sw-viewer-icon-size: 16px;
--hm-sw-viewer-disabled-opacity: 0.6;
--hm-sw-viewer-switch-off: #0a221d;
--hm-sw-viewer-switch-bevel: inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 -1px 1px rgba(0, 0, 0, 0.25);
/* Icon stroke: the SWHeader icon assets (print / K guide / layer icons) are a fill + a 1px black OUTSIDE stroke
(Figma mask "outside" path) + a 0-offset glow (stdDeviation 1.615, #061A14). Program icons are free SVGs, so the
same stroke is a drop-shadow chain that follows the icon's own contour: the four 1px shadows dilate the shape
by 1px (each one also shadows the previous ones, so corners close), the last one is the glow. */
--hm-sw-viewer-icon-outline-color: var(--hm-sw-header-text-outline-color);
--hm-sw-viewer-icon-glow: #061a14;
--hm-sw-viewer-icon-outline:
drop-shadow(1px 0 0 var(--hm-sw-viewer-icon-outline-color)) drop-shadow(-1px 0 0 var(--hm-sw-viewer-icon-outline-color))
drop-shadow(0 1px 0 var(--hm-sw-viewer-icon-outline-color)) drop-shadow(0 -1px 0 var(--hm-sw-viewer-icon-outline-color))
drop-shadow(0 0 1.615px var(--hm-sw-viewer-icon-glow));
/* Dropdown (light) and More (beige) */
--hm-sw-viewer-menu-background: #fff;
--hm-sw-viewer-menu-border-color: #dedbd2;
--hm-sw-viewer-menu-text: #0a221d;
--hm-sw-viewer-menu-muted: #6e6759;
--hm-sw-viewer-menu-highlight: rgba(189, 223, 212, 0.45);
--hm-sw-viewer-menu-highlight-text: #123328;
--hm-sw-viewer-menu-shadow: 0 0 12px rgba(0, 0, 0, 0.2);
--hm-sw-viewer-menu-item-height: 36px;
--hm-sw-viewer-more-background: #f5f0e5;
--hm-sw-viewer-more-border-color: #897445;
/* Panel */
--hm-sw-viewer-panel-background: #e0dace;
--hm-sw-viewer-panel-field-background: #f5f0e5;
--hm-sw-viewer-panel-border: #897445;
--hm-sw-viewer-panel-inner-border: #9a9284;
--hm-sw-viewer-panel-title-background: #221d13;
--hm-sw-viewer-panel-title-highlight: #a98d53;
--hm-sw-viewer-panel-title-color: #ffc600;
--hm-sw-viewer-panel-title-button-hover: #604f32;
--hm-sw-viewer-panel-text: #1d1810;
--hm-sw-viewer-panel-heading: #3e3523;
--hm-sw-viewer-panel-label: #6e6759;
--hm-sw-viewer-panel-spacing: 12px;
--hm-sw-viewer-panel-title-height: 34px;
--hm-sw-viewer-panel-shadow: 0 0 12px rgba(0, 0, 0, 0.2);
--hm-sw-viewer-panel-primary: #123328;
--hm-sw-viewer-panel-switch-off: #9a9284;
--hm-sw-viewer-panel-switch-on: #266651;
--hm-sw-viewer-panel-badge-background: #f4f1ea;
--hm-sw-viewer-panel-overlay: rgba(0, 0, 0, 0.1);
/* Close: green curve, #E40000 on hover (user-supplied reference value). */
--hm-sw-viewer-close-glow: #123328;
--hm-sw-viewer-close-edge: #020a07;
--hm-sw-viewer-close-hover: #e40000;
--hm-sw-viewer-close-icon: #999;
}
.hm-sw-viewer-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* ---------- Header ---------- */
.hm-sw-viewer-header {
container: hm-sw-viewer / inline-size;
position: relative;
box-sizing: border-box;
display: flex;
align-items: flex-start;
gap: 2px;
width: 100%;
min-width: 0;
height: var(--hm-sw-viewer-height);
border: 1px solid var(--hm-sw-viewer-border-color);
background: var(--hm-sw-viewer-background);
color: var(--hm-sw-viewer-text);
font-size: var(--hm-sw-viewer-font-size);
font-weight: 500;
line-height: 20px;
}
.hm-sw-viewer-header__brand {
position: relative;
z-index: 2;
display: flex;
align-items: center;
flex: 0 0 auto;
width: var(--hm-sw-viewer-brand-empty-width);
height: calc(var(--hm-sw-viewer-height) - 2px);
margin-right: -2px;
font-weight: 700;
}
/* With a logo: the reference 72px cap (logo at 24px), wider only for a wider logo. Without: the slanted edge only. */
.hm-sw-viewer-header__brand[data-logo] { width: auto; min-width: var(--hm-sw-viewer-brand-width); padding: 0 25px 0 24px; }
button.hm-sw-viewer-header__brand { text-align: left; }
.hm-sw-viewer-header__brand-shape { position: absolute; inset: 0 auto 0 0; width: calc(100% + 16px); height: 100%; pointer-events: none; }
.hm-sw-viewer-header__brand-mark { position: relative; display: inline-flex; align-items: center; }
.hm-sw-viewer-header__brand-mark > :is(img, svg) { display: block; max-height: 24px; width: auto; }
.hm-sw-viewer-header__document {
display: flex;
align-items: center;
flex: 0 1 clamp(140px, 25cqw, 360px);
width: clamp(220px, 25cqw, 360px);
min-width: 56px;
height: calc(var(--hm-sw-viewer-height) - 2px);
padding: 0 16px 0 24px;
border: 1px solid var(--hm-sw-header-document-border-color);
border-left: 0;
background: var(--hm-sw-header-document-background);
}
.hm-sw-viewer-header__document-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.hm-sw-viewer-header__object {
display: flex;
align-items: center;
flex: 1 0 auto;
min-width: 0;
height: calc(var(--hm-sw-viewer-height) - 2px);
gap: var(--hm-sw-viewer-gap);
padding-inline: var(--hm-sw-viewer-gap);
border: 1px solid var(--hm-sw-header-object-border-color);
border-radius: var(--hm-sw-header-object-radius);
background: var(--hm-sw-header-object-background);
}
.hm-sw-viewer-header__group {
display: flex;
align-items: center;
gap: var(--hm-sw-viewer-gap);
min-height: var(--hm-sw-viewer-control-height);
padding-inline: var(--hm-sw-viewer-gap);
}
.hm-sw-viewer-header__group[data-surface="field"] {
border: 1px solid var(--hm-sw-header-property-border-color);
border-radius: var(--hm-sw-header-property-radius);
background: var(--hm-sw-header-property-background);
box-shadow: var(--hm-sw-header-property-shadow);
}
.hm-sw-viewer-header__actions {
display: flex;
align-items: center;
align-self: stretch;
gap: var(--hm-sw-viewer-gap);
margin-left: auto;
padding-left: 6px;
}
/* Buttons, menu buttons and toggles: transparent, #266651 hover, #FFC600 pressed / open / on (no selected box). */
.hm-sw-viewer-header .hm-sw-viewer-header__button {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
gap: 8px;
height: var(--hm-sw-viewer-button-height);
padding: 6px 14px;
border: 1px solid transparent;
border-radius: var(--hm-sw-viewer-radius);
color: var(--hm-sw-viewer-text);
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.hm-sw-viewer-header .hm-sw-viewer-header__button[data-kind="toggle"] { height: var(--hm-sw-viewer-control-height); padding: 0 8px; }
.hm-sw-viewer-header .hm-sw-viewer-header__button[data-icon-only] { min-width: var(--hm-sw-viewer-button-height); padding: 0 8px; }
/* Always-icon buttons (info, More): the reference 32 x 40 icon button. */
.hm-sw-viewer-header .hm-sw-viewer-header__button[data-icon-only="always"] { min-width: 32px; width: 32px; height: var(--hm-sw-viewer-control-height); padding: 0 8px; }
.hm-sw-viewer-header .hm-sw-viewer-header__button:hover:not(:disabled) { background: var(--hm-sw-viewer-hover); }
.hm-sw-viewer-header .hm-sw-viewer-header__button:active:not(:disabled),
.hm-sw-viewer-header .hm-sw-viewer-header__button:is([data-state="open"], [data-state="on"]) { color: var(--hm-sw-viewer-accent); }
.hm-sw-viewer-header .hm-sw-viewer-header__button:is([data-state="open"], [data-state="on"]):not(:hover) { background: transparent; }
.hm-sw-viewer-header :is(.hm-sw-viewer-header__button, .hm-sw-viewer-header__tab):disabled { opacity: var(--hm-sw-viewer-disabled-opacity); cursor: not-allowed; }
.hm-sw-viewer-header__icon, .hm-sw-viewer-header__chevron { display: inline-flex; width: var(--hm-sw-viewer-icon-size); height: var(--hm-sw-viewer-icon-size); }
.hm-sw-viewer-header__icon > svg, .hm-sw-viewer-header__chevron > svg { width: 100%; height: 100%; }
/* Tabs (Model / Layout): 40px triggers with an 11px count; the active one turns #FFC600. */
.hm-sw-viewer-header__tabs { display: flex; align-items: center; gap: var(--hm-sw-viewer-gap); }
.hm-sw-viewer-header .hm-sw-viewer-header__tab {
display: inline-flex;
align-items: center;
gap: var(--hm-sw-viewer-gap);
height: var(--hm-sw-viewer-control-height);
padding: 0 8px;
border: 1px solid transparent;
border-radius: var(--hm-sw-viewer-radius);
color: var(--hm-sw-viewer-text);
font-weight: 500;
line-height: 1.4;
white-space: nowrap;
}
.hm-sw-viewer-header .hm-sw-viewer-header__tab:hover:not(:disabled) { background: var(--hm-sw-viewer-hover); }
.hm-sw-viewer-header .hm-sw-viewer-header__tab[data-state="active"] { color: var(--hm-sw-viewer-accent); }
.hm-sw-viewer-header__count { font-size: var(--hm-sw-viewer-label-size); font-weight: 400; font-variant-numeric: tabular-nums; }
/* Switch fields (2.5D, 어두운 배경): 11px muted caption above the switch. */
.hm-sw-viewer-header__switch-field {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--hm-sw-viewer-gap);
min-height: var(--hm-sw-viewer-control-height);
padding-inline: 8px;
}
.hm-sw-viewer-header__switch-label { color: var(--hm-sw-viewer-muted); font-size: var(--hm-sw-viewer-label-size); font-weight: 400; line-height: 1; white-space: nowrap; }
.hm-sw-viewer-header :is(.hm-sw-viewer-header__button, .hm-sw-viewer-header__tab, .hm-sw-viewer-header__brand, .hm-sw-viewer-switch):focus-visible {
outline: 2px solid var(--hm-sw-viewer-accent);
outline-offset: 2px;
}
/* Text and icon stroke (2026-10-19 request): the SWHeader black outline on the viewer header, same tokens and
method — an outside outline of the glyph / icon shape, never a box border.
Text: 14px = --hm-sw-header-text-outline (1.3px black), 11px captions / counts / status = --hm-sw-header-label-outline
(1px black), as SWHeader's 12px labels. Icons: --hm-sw-viewer-icon-outline (see the token). Fill color, size,
weight and spacing are unchanged; hover / active / open change only the fill, disabled fades the whole button
(opacity) like SWHeader. Dropdown faces (light / beige) and panels get no outline, like SWHeader's More menu. */
.hm-sw-viewer-header :is(
.hm-sw-viewer-header__brand-mark, .hm-sw-viewer-header__document-name, .hm-sw-viewer-header__label, .hm-sw-viewer-header__tab
) {
text-shadow: var(--hm-sw-header-text-outline);
}
.hm-sw-viewer-header :is(.hm-sw-viewer-header__count, .hm-sw-viewer-header__switch-label, .hm-sw-viewer-status__label) {
text-shadow: var(--hm-sw-header-label-outline);
}
.hm-sw-viewer-header :is(.hm-sw-viewer-header__icon, .hm-sw-viewer-header__chevron, .hm-sw-viewer-status > svg) {
filter: var(--hm-sw-viewer-icon-outline);
}
/* Clipped (ellipsis) file name: the outline may paint 2px outside the box instead of being cut. */
.hm-sw-viewer-header .hm-sw-viewer-header__document-name {
overflow: clip;
overflow-clip-margin: var(--hm-sw-header-text-outline-room);
}
/* Status */
.hm-sw-viewer-status {
display: flex;
align-items: center;
gap: var(--hm-sw-viewer-gap);
padding-inline: 8px;
color: var(--hm-sw-viewer-muted);
font-size: var(--hm-sw-viewer-label-size);
font-weight: 400;
white-space: nowrap;
}
.hm-sw-viewer-status > svg { width: var(--hm-sw-viewer-icon-size); height: var(--hm-sw-viewer-icon-size); }
.hm-sw-viewer-status[data-state="error"] { color: var(--hm-sw-viewer-accent); }
.hm-sw-viewer-status__spin { animation: hm-sw-viewer-spin 1s linear infinite; }
@keyframes hm-sw-viewer-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .hm-sw-viewer-status__spin { animation: none; } }
/* Narrow header (reference breakpoints, measured on the header itself). */
@container hm-sw-viewer (max-width: 1179.98px) {
.hm-sw-viewer-header__object { gap: 2px; padding-inline: 2px; }
.hm-sw-viewer-header__group { gap: 2px; padding-inline: 2px; }
.hm-sw-viewer-header__actions { padding-left: 2px; gap: 2px; }
.hm-sw-viewer-status { padding-inline: 2px; }
}
@container hm-sw-viewer (max-width: 559.98px) {
.hm-sw-viewer-header__document { padding-right: 8px; }
.hm-sw-viewer-status__label { display: none; }
}
/* ---------- Switch (header / panel faces) ---------- */
.hm-sw-theme .hm-sw-viewer-switch {
position: relative;
display: inline-flex;
align-items: center;
flex-shrink: 0;
width: 36px;
height: 20px;
padding: 2px;
border-radius: 9999px;
background: var(--hm-sw-viewer-switch-off);
box-shadow: var(--hm-sw-viewer-switch-bevel);
transition: background-color 0.15s;
}
.hm-sw-viewer-switch__thumb { display: block; width: 16px; height: 16px; border-radius: 9999px; background: var(--hm-sw-viewer-text); transition: transform 0.15s; }
.hm-sw-viewer-switch[data-state="checked"] { background: var(--hm-sw-viewer-accent); }
.hm-sw-viewer-switch[data-state="checked"] .hm-sw-viewer-switch__thumb { transform: translateX(16px); background: var(--hm-sw-viewer-switch-off); }
.hm-sw-theme .hm-sw-viewer-switch:disabled { opacity: var(--hm-sw-viewer-disabled-opacity); cursor: not-allowed; }
.hm-sw-viewer-panel .hm-sw-viewer-switch { background: var(--hm-sw-viewer-panel-switch-off); box-shadow: none; }
.hm-sw-viewer-panel .hm-sw-viewer-switch .hm-sw-viewer-switch__thumb { background: #fff; }
.hm-sw-viewer-panel .hm-sw-viewer-switch[data-state="checked"] { background: var(--hm-sw-viewer-panel-switch-on); }
@media (prefers-reduced-motion: reduce) { .hm-sw-viewer-switch, .hm-sw-viewer-switch__thumb { transition: none; } }
/* ---------- Dropdown ---------- */
.hm-sw-viewer-menu {
position: fixed;
z-index: 60;
display: flex;
flex-direction: column;
min-width: 160px;
max-width: calc(100vw - 8px);
max-height: min(640px, calc(100dvh - 8px));
overflow-y: auto;
padding: 4px;
border: 1px solid var(--hm-sw-viewer-menu-border-color);
border-radius: var(--hm-sw-viewer-radius);
background: var(--hm-sw-viewer-menu-background);
box-shadow: var(--hm-sw-viewer-menu-shadow);
color: var(--hm-sw-viewer-menu-text);
font-size: 14px;
font-weight: 400;
line-height: 20px;
outline: none;
}
.hm-sw-viewer-menu[data-surface="beige"] { background: var(--hm-sw-viewer-more-background); border-color: var(--hm-sw-viewer-more-border-color); }
.hm-sw-viewer-menu .hm-sw-viewer-menu__item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-height: var(--hm-sw-viewer-menu-item-height);
padding: 0 12px;
border-radius: 2px;
color: inherit;
text-align: left;
white-space: nowrap;
outline: none;
}
.hm-sw-viewer-menu .hm-sw-viewer-menu__item:is(:focus, :hover, [data-state="open"]):not([aria-disabled]) { background: var(--hm-sw-viewer-menu-highlight); color: var(--hm-sw-viewer-menu-highlight-text); }
.hm-sw-viewer-menu .hm-sw-viewer-menu__item[aria-disabled] { opacity: 0.5; cursor: not-allowed; }
.hm-sw-viewer-menu .hm-sw-viewer-menu__item:focus-visible { outline: none; }
.hm-sw-viewer-menu__icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 16px; width: 16px; height: 16px; }
.hm-sw-viewer-menu__icon:empty { display: none; }
.hm-sw-viewer-menu__item[role="menuitemcheckbox"] .hm-sw-viewer-menu__icon,
.hm-sw-viewer-menu__item[role="menuitemradio"] .hm-sw-viewer-menu__icon { display: inline-flex; }
.hm-sw-viewer-menu__icon > svg { width: 16px; height: 16px; }
.hm-sw-viewer-menu__dot { width: 8px; height: 8px; border-radius: 9999px; background: currentColor; }
.hm-sw-viewer-menu__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.hm-sw-viewer-menu__shortcut { margin-left: auto; color: var(--hm-sw-viewer-menu-muted); font-size: 12px; letter-spacing: 0.05em; }
.hm-sw-viewer-menu__chevron { display: inline-flex; margin-left: auto; color: var(--hm-sw-viewer-menu-muted); }
.hm-sw-viewer-menu__label { padding: 6px 12px; color: var(--hm-sw-viewer-menu-muted); font-size: 12px; font-weight: 500; line-height: 18px; }
.hm-sw-viewer-menu__separator { flex: 0 0 1px; height: 1px; margin: 4px -4px; background: var(--hm-sw-viewer-menu-border-color); }
.hm-sw-viewer-menu[data-surface="beige"] .hm-sw-viewer-menu__separator { background: var(--hm-sw-viewer-panel-inner-border); }
/* ---------- Panel ---------- */
.hm-sw-viewer-panel {
display: flex;
flex-direction: column;
max-width: 90vw;
max-height: 90vh;
overflow: hidden;
border: 2px solid var(--hm-sw-viewer-panel-border);
border-radius: var(--hm-sw-viewer-radius);
background: var(--hm-sw-viewer-panel-background);
box-shadow: var(--hm-sw-viewer-panel-shadow), inset 0 0 0 1px var(--hm-sw-viewer-panel-field-background);
color: var(--hm-sw-viewer-panel-text);
font-size: 14px;
font-weight: 400;
line-height: 20px;
outline: none;
}
.hm-sw-viewer-panel-backdrop { inset: 0; background: var(--hm-sw-viewer-panel-overlay); }
.hm-sw-viewer-panel__titlebar {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 4px;
min-height: var(--hm-sw-viewer-panel-title-height);
padding-left: var(--hm-sw-viewer-panel-spacing);
border-bottom: 1px solid var(--hm-sw-viewer-panel-border);
background: var(--hm-sw-viewer-panel-title-background);
box-shadow: inset 0 1px 0 var(--hm-sw-viewer-panel-title-highlight);
user-select: none;
}
.hm-sw-viewer-panel__titlebar[data-draggable] { cursor: grab; touch-action: none; }
.hm-sw-viewer-panel__titlebar[data-dragging] { cursor: grabbing; }
.hm-sw-viewer-panel__title {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 0;
margin: 0;
color: var(--hm-sw-viewer-panel-title-color);
font-size: 14px;
font-weight: 700;
line-height: 1.4;
}
.hm-sw-viewer-panel__title-icon { display: inline-flex; flex: 0 0 16px; width: 16px; height: 16px; }
.hm-sw-viewer-panel__title-icon > svg { width: 100%; height: 100%; }
.hm-sw-viewer-panel__title-text { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.hm-sw-viewer-panel__actions { display: flex; flex-shrink: 0; align-items: center; }
.hm-sw-viewer-panel :is(.hm-sw-viewer-panel__icon-button, .hm-sw-viewer-button[data-variant="ghost"]) {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--hm-sw-viewer-panel-title-height);
height: var(--hm-sw-viewer-panel-title-height);
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
color: var(--hm-sw-viewer-muted);
}
.hm-sw-viewer-panel :is(.hm-sw-viewer-panel__icon-button, .hm-sw-viewer-button[data-variant="ghost"]):hover:not(:disabled) { background: var(--hm-sw-viewer-panel-title-button-hover); color: var(--hm-sw-viewer-panel-title-color); }
.hm-sw-viewer-panel :is(.hm-sw-viewer-panel__icon-button, .hm-sw-viewer-button[data-variant="ghost"], .hm-sw-viewer-panel__close):focus-visible { outline: 2px solid var(--hm-sw-viewer-panel-title-color); outline-offset: -3px; }
.hm-sw-viewer-panel .hm-sw-viewer-panel__close {
position: relative;
isolation: isolate;
display: inline-flex;
align-items: center;
flex-shrink: 0;
width: calc(var(--hm-sw-viewer-panel-title-height) * 1.53);
height: var(--hm-sw-viewer-panel-title-height);
padding: 0 0 0 calc(var(--hm-sw-viewer-panel-title-height) * 0.7);
overflow: hidden;
color: var(--hm-sw-viewer-close-icon);
}
.hm-sw-viewer-panel__close-shape { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.hm-sw-viewer-panel__close-curve { stroke: var(--hm-sw-viewer-panel-border); stroke-width: 1; }
.hm-sw-viewer-panel__close-icon { position: relative; width: 16px; height: 16px; pointer-events: none; }
.hm-sw-viewer-panel .hm-sw-viewer-panel__close:focus-visible { --hm-sw-viewer-close-glow: #266651; color: #fff; }
.hm-sw-viewer-panel .hm-sw-viewer-panel__close:is(:hover, :active) { --hm-sw-viewer-close-glow: var(--hm-sw-viewer-close-hover); --hm-sw-viewer-close-edge: var(--hm-sw-viewer-close-hover); color: #fff; }
.hm-sw-viewer-panel__body {
display: flex;
flex-direction: column;
gap: 20px;
flex: 1;
min-height: 0;
overflow: auto;
overscroll-behavior: contain;
padding: var(--hm-sw-viewer-panel-spacing);
scrollbar-width: thin;
scrollbar-color: var(--hm-sw-viewer-panel-inner-border) var(--hm-sw-viewer-panel-background);
}
.hm-sw-viewer-panel__footer {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
gap: 8px;
padding: 8px var(--hm-sw-viewer-panel-spacing);
border-top: 1px solid var(--hm-sw-viewer-panel-inner-border);
background: var(--hm-sw-viewer-panel-background);
}
/* Panel content */
.hm-sw-viewer-section { display: flex; flex-direction: column; gap: 12px; }
.hm-sw-viewer-section[data-surface="card"] {
padding: var(--hm-sw-viewer-panel-spacing);
border: 1px solid var(--hm-sw-viewer-panel-inner-border);
border-radius: var(--hm-sw-viewer-radius);
background: var(--hm-sw-viewer-panel-field-background);
box-shadow: inset 0 1px 0 #fff;
}
.hm-sw-viewer-section__title { margin: 0; color: var(--hm-sw-viewer-panel-heading); font-size: 12px; font-weight: 700; line-height: 16px; letter-spacing: 0.025em; text-transform: uppercase; }
.hm-sw-viewer-hint { margin: 0; color: var(--hm-sw-viewer-panel-heading); font-size: 12px; line-height: 16px; }
.hm-sw-viewer-field { display: flex; flex-direction: column; gap: 6px; }
.hm-sw-viewer-field[data-disabled] { opacity: 0.5; pointer-events: none; }
.hm-sw-viewer-field__row { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 20px; }
.hm-sw-viewer-field__label { min-width: 0; color: var(--hm-sw-viewer-panel-label); font-size: 14px; font-weight: 500; line-height: 20px; }
.hm-sw-viewer-field__control { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.hm-sw-viewer-field__unit { color: var(--hm-sw-viewer-panel-label); font-size: 12px; }
.hm-sw-theme .hm-sw-viewer-input {
box-sizing: border-box;
width: 80px;
height: 28px;
padding: 0 10px;
border: 1px solid var(--hm-sw-viewer-panel-inner-border);
border-radius: var(--hm-sw-viewer-radius);
background: var(--hm-sw-viewer-panel-field-background);
color: var(--hm-sw-viewer-panel-text);
font: inherit;
font-size: 12px;
line-height: 18px;
}
.hm-sw-theme .hm-sw-viewer-input:focus-visible { outline: 2px solid var(--hm-sw-viewer-panel-switch-on); outline-offset: -1px; }
.hm-sw-theme .hm-sw-viewer-input:disabled { opacity: 0.5; }
.hm-sw-theme .hm-sw-viewer-slider {
--hm-sw-viewer-slider-percent: 0%;
width: 100%;
height: 18px;
margin: 0;
background: transparent;
appearance: none;
cursor: pointer;
}
.hm-sw-viewer-slider::-webkit-slider-runnable-track { height: 6px; border-radius: 9999px; background: linear-gradient(90deg, var(--hm-sw-viewer-panel-switch-on) var(--hm-sw-viewer-slider-percent), var(--hm-sw-viewer-panel-background) var(--hm-sw-viewer-slider-percent)); }
.hm-sw-viewer-slider::-moz-range-track { height: 6px; border-radius: 9999px; background: var(--hm-sw-viewer-panel-background); }
.hm-sw-viewer-slider::-moz-range-progress { height: 6px; border-radius: 9999px; background: var(--hm-sw-viewer-panel-switch-on); }
.hm-sw-viewer-slider::-webkit-slider-thumb { width: 18px; height: 18px; margin-top: -6px; border: 2px solid var(--hm-sw-viewer-panel-switch-on); border-radius: 9999px; background: #fff; appearance: none; }
.hm-sw-viewer-slider::-moz-range-thumb { box-sizing: border-box; width: 18px; height: 18px; border: 2px solid var(--hm-sw-viewer-panel-switch-on); border-radius: 9999px; background: #fff; }
.hm-sw-theme .hm-sw-viewer-slider:focus-visible { outline: 2px solid var(--hm-sw-viewer-panel-switch-on); outline-offset: 2px; }
.hm-sw-theme .hm-sw-viewer-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
height: 24px;
padding: 0 8px;
border: 1px solid var(--hm-sw-viewer-panel-primary);
border-radius: var(--hm-sw-viewer-radius);
background: #fff;
color: var(--hm-sw-viewer-panel-primary);
font-size: 12px;
font-weight: 500;
line-height: 16px;
white-space: nowrap;
}
.hm-sw-theme .hm-sw-viewer-button[data-variant="outline"]:hover:not(:disabled) { background: var(--hm-sw-viewer-panel-field-background); }
.hm-sw-theme .hm-sw-viewer-button[data-variant="primary"] { background: var(--hm-sw-viewer-panel-primary); color: #fff; }
.hm-sw-theme .hm-sw-viewer-button[data-variant="primary"]:hover:not(:disabled) { background: var(--hm-sw-viewer-hover); border-color: var(--hm-sw-viewer-hover); }
.hm-sw-theme .hm-sw-viewer-button:focus-visible { outline: 2px solid var(--hm-sw-viewer-panel-switch-on); outline-offset: 1px; }
.hm-sw-theme .hm-sw-viewer-button:disabled { opacity: 0.5; cursor: not-allowed; }
.hm-sw-viewer-button__icon { display: inline-flex; width: 16px; height: 16px; }
.hm-sw-viewer-button__icon > svg { width: 100%; height: 100%; }
.hm-sw-viewer-badge { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: var(--hm-sw-viewer-radius); background: var(--hm-sw-viewer-panel-badge-background); color: var(--hm-sw-viewer-panel-label); font-size: 12px; font-weight: 500; line-height: 18px; font-variant-numeric: tabular-nums; }
/* Corner resize grip (replaces native resize: same corner and glyph idea, works in every browser and with
pointer capture; the size stays within min / 90% of the bounds / the bounds edge). */
.hm-sw-viewer-panel__resize {
position: absolute;
right: 0;
bottom: 0;
z-index: 1;
width: 14px;
height: 14px;
cursor: nwse-resize;
touch-action: none;
background:
linear-gradient(135deg, transparent 0 55%, var(--hm-sw-viewer-panel-inner-border) 55% 62%, transparent 62% 72%, var(--hm-sw-viewer-panel-inner-border) 72% 79%, transparent 79%);
}