/* HopToDesk dashboard component layer.
   Every repeated UI area lives here. Do not hand-roll a new one in markup.
   See CLAUDE.md, "Dashboard UI components". */

:root {
   --hd-surface: #FFFFFF;
   --hd-surface-2: #F8F9FA;
   --hd-surface-3: #EDF0F3;
   --hd-line: #E6E9ED;
   --hd-line-strong: #CED4DA;
   --hd-ink: #212529;
   --hd-ink-2: #6C757D;
   --hd-ink-3: #ADB5BD;

   --hd-primary: #0D6EFD;
   --hd-primary-hover: #0B5ED7;
   --hd-danger: #DC3545;
   --hd-danger-hover: #BB2D3B;
   --hd-danger-line: #F0B4BA;
   --hd-info-line: #B6CFF5;
   --hd-ok-line: #A9DDBF;

   --hd-warn-bg: #FFF3EA;   --hd-warn-ink: #F2670C;
   --hd-info-bg: #EAF2FE;   --hd-info-ink: #1668E3;
   --hd-ok-bg: #E9F7EF;     --hd-ok-ink: #17954E;
   --hd-danger-bg: #FDECE8; --hd-danger-ink: #E8481E;
   --hd-neutral-bg: #F1F3F5; --hd-neutral-ink: #6C757D;

   --hd-r-sm: 8px;
   --hd-r: 10px;
   --hd-r-lg: 12px;
}

body.dark {
   --hd-surface: #1F2937;
   --hd-surface-2: #111827;
   --hd-surface-3: #374151;
   --hd-line: #374151;
   --hd-line-strong: #4B5563;
   --hd-ink: #F3F4F6;
   --hd-ink-2: #9CA3AF;
   --hd-ink-3: #6B7280;

   --hd-primary: #3B82F6;
   --hd-primary-hover: #2563EB;
   --hd-danger: #EF4444;
   --hd-danger-hover: #DC2626;
   --hd-danger-line: #7F1D1D;
   --hd-info-line: #1E3A8A;
   --hd-ok-line: #14532D;

   --hd-warn-bg: rgba(242, 103, 12, .15);   --hd-warn-ink: #FFA05C;
   --hd-info-bg: rgba(22, 104, 227, .15);   --hd-info-ink: #79ABF8;
   --hd-ok-bg: rgba(23, 149, 78, .15);      --hd-ok-ink: #5FD08D;
   --hd-danger-bg: rgba(232, 72, 30, .15);  --hd-danger-ink: #F79379;
   --hd-neutral-bg: rgba(255, 255, 255, .07); --hd-neutral-ink: #9CA3AF;
}

/* ---------------------------------------------------------------- note
   A message the reader consumes. Soft tint, no border, small icon.
   The icon is drawn by ::before, so converting a legacy box is a class
   swap and nothing else -- no markup edit, no risk to the i18n keys.
   Never a container for controls: that is .hd-panel. */
.hd-note {
   position: relative;
   padding: 12px 14px 12px 41px;
   border-radius: var(--hd-r);
   font-size: .875rem;
   line-height: 1.5;
}
.hd-note::before {
   content: "";
   position: absolute; left: 14px; top: 12px;
   width: 17px;
   /* The box is the height of the first line, and the glyph is centred inside
      it, so the icon sits on the middle of that line rather than at the top of
      the note. */
   height: 1.3125rem;
   background: currentColor;
   -webkit-mask: var(--hd-note-icon) center / 17px 17px no-repeat;
   mask: var(--hd-note-icon) center / 17px 17px no-repeat;
}
/* A note that carries an action on the same row is taller than one line, and
   there the icon reads as misaligned unless it centres against the whole row. */
.hd-note:has(> .hd-btn)::before,
.hd-note:has(> a.hd-btn)::before,
.hd-note-center::before {
   top: 50%; height: 17px; transform: translateY(-50%);
}
/* Legacy boxes carry their own inline icon, either as the first child or
   as the first child of a flex wrapper. ::before replaces it. */
.hd-note > svg:first-child,
.hd-note > :first-child > svg:first-child { display: none; }
.hd-note-t { display: block; font-weight: 600; margin-bottom: 3px; }
.hd-note p { margin: 0; }
.hd-note p + p, .hd-note p + div, .hd-note div + p { margin-top: 6px; }
.hd-note ul, .hd-note ol { margin: 6px 0 0; padding-left: 18px; list-style-position: outside; }
.hd-note ul { list-style-type: disc; }
.hd-note li + li { margin-top: 4px; }
.hd-note a { text-decoration: underline; }
.hd-note a.hd-btn { text-decoration: none; }
.hd-note strong, .hd-note b { font-weight: 600; }
.hd-note code, .hd-note kbd {
   background: rgba(255, 255, 255, .68);
   border-radius: 5px;
   padding: 1px 5px;
   font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
   font-size: .875rem;
}
.hd-note pre {
   margin: 8px 0 0;
   padding: 10px;
   border-radius: 6px;
   background: rgba(255, 255, 255, .68);
   font-size: .875rem;
   overflow-x: auto;
}
body.dark .hd-note code,
body.dark .hd-note kbd,
body.dark .hd-note pre { background: rgba(255, 255, 255, .1); }
.hd-note summary { cursor: pointer; font-weight: 600; margin-top: 8px; }
.hd-note-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; align-items: center; }

.hd-note-warn {
   background: var(--hd-warn-bg); color: var(--hd-warn-ink);
   --hd-note-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
}
.hd-note-info {
   background: var(--hd-info-bg); color: var(--hd-info-ink);
   --hd-note-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 7.6h.01'/%3E%3C/svg%3E");
}
.hd-note-ok {
   background: var(--hd-ok-bg); color: var(--hd-ok-ink);
   --hd-note-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
}
.hd-note-danger {
   background: var(--hd-danger-bg); color: var(--hd-danger-ink);
   --hd-note-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Cpath d='M15 9l-6 6M9 9l6 6'/%3E%3C/svg%3E");
}
.hd-note-quiet {
   background: var(--hd-neutral-bg); color: var(--hd-neutral-ink);
   --hd-note-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Cpath d='M12 11v5'/%3E%3Cpath d='M12 7.6h.01'/%3E%3C/svg%3E");
}
/* A note inside a note is always a mistake of the old markup. Neutralise it
   rather than stacking two tints. */
.hd-note .hd-note {
   background: rgba(0, 0, 0, .05);
   color: inherit;
   padding-left: 14px;
}
.hd-note .hd-note::before { display: none; }
body.dark .hd-note .hd-note { background: rgba(255, 255, 255, .07); }

/* A one-line note with its action pushed to the right edge. */
.hd-note-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.hd-note-row > :not(.hd-btn) { flex: 1 1 240px; min-width: 0; }
.hd-note-row > .hd-btn { margin-left: auto; flex-shrink: 0; }

/* A note that carries no icon at all: a bare tinted strip. */
.hd-note-bare { padding-left: 14px; }
.hd-note-bare::before { display: none; }

/* Inside a note the note owns the ink. Legacy boxes carry per-element
   text-amber-800 / text-blue-900 classes; this beats them on specificity
   so the conversion never has to touch a child's class attribute.

   Anything that paints its own background is excluded, and so is anything
   explicitly set to white: a filled button inside a note has a white label
   on a solid fill, and forcing it to the note's ink made it invisible on
   the past-due banner, the empty-devices download button and the custom
   build upgrade link. */
.hd-note :is(p, span, div, li, ul, ol, h2, h3, h4, h5, h6, strong, b, em, small,
             label, summary, dt, dd, a, td, th):not(.hd-btn):not(.hd-pill):not(.hd-icon):not([class~="text-white"]):not([class~="dark:text-white"]):not([class*=" bg-"]):not([class^="bg-"]):not([style*="color"]) { color: inherit; }

/* ------------------------------------------------------- panel & card
   A container that holds controls or content. Always a grey keyline on a
   plain surface. Never tinted, never a gradient, never a 2px border. */
.hd-card,
.hd-panel {
   background: var(--hd-surface);
   border: 1px solid var(--hd-line);
   border-radius: var(--hd-r-lg);
}
.hd-card { padding: 20px; }
/* A whole card that is one link: icon chip, title, one line. */
.hd-card-link {
   display: flex; align-items: center; gap: 12px;
   color: inherit; text-decoration: none; cursor: pointer;
   transition: border-color .15s, box-shadow .15s, transform .15s;
}
.hd-card-link:hover {
   border-color: var(--hd-line-strong);
   box-shadow: 0 4px 14px rgba(0, 0, 0, .08);
   transform: translateY(-1px);
}
.hd-card-link:hover .hd-card-link-t { color: var(--hd-primary); }
.hd-card-link-t { display: block; font-size: .9375rem; font-weight: 600; color: var(--hd-ink); }
.hd-card-link-sub { display: block; font-size: .875rem; color: var(--hd-ink-2); }
.hd-panel { padding: 18px 20px; }

.hd-panel-hd { display: flex; align-items: center; gap: 12px; }
.hd-panel-hd h3, .hd-panel-hd h4 {
   margin: 0; font-size: .9375rem; font-weight: 600; color: var(--hd-ink);
}
.hd-panel-hd p { margin: 2px 0 0; font-size: .875rem; color: var(--hd-ink-2); }
.hd-panel-ctl { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.hd-panel-bd {
   margin-top: 14px; padding-top: 14px;
   border-top: 1px solid var(--hd-line);
   font-size: .875rem; color: var(--hd-ink-2); line-height: 1.55;
}

/* ---------------------------------------------------------- icon chip */
.hd-icon {
   width: 38px; height: 38px; flex: 0 0 auto;
   border-radius: var(--hd-r);
   display: flex; align-items: center; justify-content: center;
   background: var(--hd-neutral-bg); color: var(--hd-neutral-ink);
}
.hd-icon > svg { width: 19px; height: 19px; }
.hd-icon-sm { width: 30px; height: 30px; border-radius: var(--hd-r-sm); }
.hd-icon-sm > svg { width: 15px; height: 15px; }
.hd-icon-info   { background: var(--hd-info-bg);   color: var(--hd-info-ink); }
.hd-icon-ok     { background: var(--hd-ok-bg);     color: var(--hd-ok-ink); }
.hd-icon-warn   { background: var(--hd-warn-bg);   color: var(--hd-warn-ink); }
.hd-icon-danger { background: var(--hd-danger-bg); color: var(--hd-danger-ink); }

/* ----------------------------------------------------------- OS icon
   Windows fills its 24x24 viewBox edge to edge; the Apple and Tux
   silhouettes carry roughly 15 to 20 percent inner padding. Two helpers
   used to compensate by drawing Mac and Linux one size step larger, which
   is why a row of OS icons never lined up. The box is one size for every
   platform now and only the glyph is scaled. */
.hd-os { width: 18px; height: 18px; flex: 0 0 auto; }
.hd-os-sm { width: 14px; height: 14px; }
.hd-os-lg { width: 20px; height: 20px; }
.hd-os-pad { transform: scale(1.18); }

/* ------------------------------------------------------------- avatar
   Contact initials. These used to be a green gradient circle; removing the
   gradient left white text on a white circle, so the initials vanished. */
.hd-avatar {
   flex: 0 0 auto;
   width: 36px; height: 36px;
   border-radius: 50%;
   display: flex; align-items: center; justify-content: center;
   background: var(--hd-ok-ink); color: #fff;
   font-weight: 700; font-size: .875rem; line-height: 1;
}
.hd-avatar-md { width: 48px; height: 48px; font-size: 1rem; }
.hd-avatar-lg { width: 56px; height: 56px; font-size: 1.125rem; }

/* --------------------------------------------------------- stat tile
   84 tiles existed in 18 shapes: five paddings, four number sizes and
   three radii, which is why rows of them never lined up. The container is
   block by default so a value-over-label tile keeps working untouched;
   hd-tile-i turns on the icon row. */
.hd-tile {
   background: var(--hd-surface);
   border: 1px solid var(--hd-line);
   border-radius: var(--hd-r);
   padding: 14px 16px;
   min-height: 76px;
   display: flex;
   flex-direction: column;
   justify-content: center;
}
.hd-tile-i { flex-direction: row; align-items: center; gap: 12px; }
.hd-tile-i > div:last-child { min-width: 0; }
.hd-tile .hd-icon { width: 36px; height: 36px; border-radius: 9px; }
.hd-tile .hd-icon > svg { width: 18px; height: 18px; }
.hd-tile-v {
   font-size: 1.5rem; font-weight: 700; line-height: 1.15;
   letter-spacing: -.02em;
}
.hd-tile-l { font-size: .875rem; margin-top: 3px; }
/* Colour as a default, so a value or label carrying its own utility keeps it. */
:where(.hd-tile-v) { color: var(--hd-ink); }
:where(.hd-tile-l) { color: var(--hd-ink-2); }
.hd-tile-i .hd-tile-l { margin-top: 2px; }
.hd-tile-v.is-ok     { color: var(--hd-ok-ink); }
.hd-tile-v.is-warn   { color: var(--hd-warn-ink); }
.hd-tile-v.is-danger { color: var(--hd-danger-ink); }
.hd-tile-v.is-info   { color: var(--hd-info-ink); }
.hd-tile-v.is-muted  { color: var(--hd-ink-3); }

/* --------------------------------------------------------------- pill */
.hd-pill {
   display: inline-flex; align-items: center; gap: 6px;
   padding: 4px 10px;
   border-radius: var(--hd-r-sm);
   font-size: .875rem; font-weight: 500; line-height: 1.25;
   white-space: nowrap;
}
.hd-pill > svg { width: 13px; height: 13px; flex: 0 0 auto; }
.hd-pill-warn    { background: var(--hd-warn-bg);    color: var(--hd-warn-ink); }
.hd-pill-info    { background: var(--hd-info-bg);    color: var(--hd-info-ink); }
.hd-pill-ok      { background: var(--hd-ok-bg);      color: var(--hd-ok-ink); }
.hd-pill-danger  { background: var(--hd-danger-bg);  color: var(--hd-danger-ink); }
.hd-pill-neutral { background: var(--hd-neutral-bg); color: var(--hd-neutral-ink); }

/* ------------------------------------------------------------- button
   Four roles, two sizes, one radius. Nothing else. */
.hd-btn {
   display: inline-flex; align-items: center; justify-content: center; gap: 7px;
   min-height: 36px; padding: 6px 16px;
   border: 1px solid transparent;
   border-radius: var(--hd-r-sm);
   font-family: inherit; font-size: .875rem; font-weight: 500;
   white-space: nowrap; cursor: pointer;
   transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.hd-btn > svg { width: 15px; height: 15px; flex: 0 0 auto; }
.hd-btn:disabled, .hd-btn.is-disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.hd-btn:focus-visible { outline: 2px solid var(--hd-primary); outline-offset: 2px; }

.hd-btn-sm { min-height: 28px; padding: 3px 11px; font-size: .875rem; }
.hd-btn-sm > svg { width: 13px; height: 13px; }
.hd-btn-icon { width: 32px; min-height: 32px; height: 32px; padding: 0; }
.hd-btn-icon.hd-btn-sm { width: 28px; min-height: 28px; height: 28px; }
.hd-btn-block { width: 100%; }

.hd-btn-primary { background: var(--hd-primary); color: #fff; }
.hd-btn-primary:hover:not(:disabled) { background: var(--hd-primary-hover); }
.hd-btn-secondary { background: var(--hd-surface); color: var(--hd-ink); border-color: var(--hd-line-strong); }
.hd-btn-secondary:hover:not(:disabled) { background: var(--hd-surface-2); }
.hd-btn-danger { background: var(--hd-danger); color: #fff; }
.hd-btn-danger:hover:not(:disabled) { background: var(--hd-danger-hover); }
.hd-btn-danger-quiet { background: var(--hd-surface); color: var(--hd-danger); border-color: var(--hd-danger-line); }
.hd-btn-danger-quiet:hover:not(:disabled) { background: var(--hd-danger-bg); }
.hd-btn-info-quiet { background: var(--hd-surface); color: var(--hd-info-ink); border-color: var(--hd-info-line); }
.hd-btn-info-quiet:hover:not(:disabled) { background: var(--hd-info-bg); }
.hd-btn-ok-quiet { background: var(--hd-surface); color: var(--hd-ok-ink); border-color: var(--hd-ok-line); }
.hd-btn-ok-quiet:hover:not(:disabled) { background: var(--hd-ok-bg); }
.hd-btn-ghost { background: transparent; color: var(--hd-ink-2); }
.hd-btn-ghost:hover:not(:disabled) { background: var(--hd-surface-3); color: var(--hd-ink); }
/* Sitting on a dark code block, where both a solid fill and a grey ghost read
   badly. */
/* Text and controls on a permanently dark surface: a video shell, a code block,
   a terminal. Grey scales meant for a white page read at about 2:1 there. */
.hd-on-dark { color: #CBD5E1; }
.hd-on-dark-dim { color: #94A3B8; }
.hd-btn-on-dark { background: rgba(255, 255, 255, .1); color: #CBD5E1; }
.hd-btn-on-dark:hover:not(:disabled) { background: rgba(255, 255, 255, .2); color: #fff; }

/* --------------------------------------------------------- copy field
   A value the reader copies. Icon only when the value is on screen. */
.hd-copy { display: flex; align-items: center; gap: 6px; }
.hd-copy-val {
   flex: 1; min-width: 0;
   background: var(--hd-surface);
   border: 1px solid var(--hd-line-strong);
   border-radius: var(--hd-r-sm);
   /* Sized to the code block it replaces, so a URL box and a command block
      are the same height and the same monospace face. */
   min-height: 52px;
   display: flex; align-items: center;
   padding: 12px 14px;
   font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
   font-size: .875rem; line-height: 1.4; color: var(--hd-ink);
   overflow-x: auto; white-space: nowrap;
}
.hd-copy-val > pre, .hd-copy-val > code {
   margin: 0; padding: 0; background: none; border: 0;
   font: inherit; color: inherit; white-space: nowrap;
}
/* An input used as the value box needs the same shape without the flex. */
input.hd-copy-val, textarea.hd-copy-val { display: block; }
.hd-copy-btn {
   width: 34px; height: 34px; flex: 0 0 auto;
   display: flex; align-items: center; justify-content: center;
   border: 0; background: transparent;
   border-radius: var(--hd-r-sm);
   color: var(--hd-primary); cursor: pointer;
}
.hd-copy-btn:hover { background: var(--hd-surface-3); color: var(--hd-primary-hover); }
.hd-copy-btn > svg { width: 17px; height: 17px; }
.hd-copy-btn.is-done { color: var(--hd-ok-ink); }

.hd-code { position: relative; }
.hd-code pre {
   margin: 0;
   background: #0F172A; color: #E2E8F0;
   border-radius: var(--hd-r);
   padding: 14px 46px 14px 14px;
   font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
   font-size: .875rem; line-height: 1.5;
   overflow-x: auto;
}
.hd-code .hd-copy-btn { position: absolute; top: 7px; right: 7px; color: #94A3B8; }
.hd-code .hd-copy-btn:hover { background: rgba(255, 255, 255, .12); color: #fff; }

/* --------------------------------------------------------- form field */
.hd-label { display: block; font-size: .875rem; font-weight: 500; color: var(--hd-ink); margin-bottom: 6px; }
.hd-input, .hd-select, .hd-textarea {
   background: var(--hd-surface);
   border: 1px solid var(--hd-line-strong);
   border-radius: var(--hd-r-sm);
   /* Same 36px as a default button, so a toolbar row of fields and buttons is
      one height. A flex row stretches a button to its tallest sibling, which
      is how the two IP-allowlist buttons ended up 38px tall. */
   min-height: 36px;
   padding: 7px 12px;
   font-family: inherit; font-size: .875rem; color: var(--hd-ink);
}
.hd-input:focus, .hd-select:focus, .hd-textarea:focus {
   outline: 0; border-color: var(--hd-primary);
   box-shadow: 0 0 0 3px color-mix(in srgb, var(--hd-primary) 18%, transparent);
}
.hd-input:disabled, .hd-select:disabled, .hd-textarea:disabled { background: var(--hd-surface-2); color: var(--hd-ink-3); }
.hd-input-sm, .hd-select-sm, .hd-textarea-sm { min-height: 28px; padding: 3px 8px; font-size: .875rem; }
/* A field with something sitting inside it: a search icon on the left, or a
   reveal/clear button on the right. Without these the control's text runs
   straight under the icon. */
/* One search field. Nine of them existed at two heights, four carrying a
   hand-placed magnifier at four different sizes and five carrying none. The
   field draws its own icon, so there is no wrapper and no svg to get wrong. */
.hd-search {
   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236C757D' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E");
   background-repeat: no-repeat;
   background-position: left 13px center;
   background-size: 17px 17px;
   padding-left: 40px;
}
.hd-input-icon { padding-left: 42px; }
.hd-input-sm.hd-input-icon { padding-left: 32px; }
.hd-input-affix { padding-right: 40px; }
.hd-input-sm.hd-input-affix { padding-right: 32px; }
.hd-textarea { min-height: 76px; padding: 10px 12px; }
.hd-textarea-sm { min-height: 56px; }
.hd-hint { font-size: .875rem; color: var(--hd-ink-2); margin-top: 6px; }
.hd-err { font-size: .875rem; color: var(--hd-danger); margin-top: 6px; }

.hd-file {
   width: 100%;
   background: var(--hd-surface);
   border: 1px solid var(--hd-line-strong);
   border-radius: var(--hd-r-sm);
   padding: 7px 12px;
   font-size: .875rem; color: var(--hd-ink);
}
.hd-file::file-selector-button {
   margin-right: 12px;
   border: 0; border-radius: 6px;
   padding: 5px 12px;
   background: var(--hd-info-bg); color: var(--hd-info-ink);
   font-family: inherit; font-size: .875rem; font-weight: 500;
   cursor: pointer;
}
.hd-file:hover::file-selector-button { background: color-mix(in srgb, var(--hd-info-ink) 16%, transparent); }


/* ---------------------------------------------------- checkbox & radio
   155 checkboxes existed at two sizes: 140 at whatever the browser drew,
   15 at 16px. One size, and the larger one, because they are easier to
   hit and easier to read. accent-color paints the native control, so the
   markup stays a real input and keeps its keyboard behaviour. */
/* Zero specificity, so a mt-0.5 or ml-4 in the markup still lands. A plain
   `margin: 0` here outranked every Tailwind margin utility and left boxes
   sitting high beside a two-line label. */
:where(.hd-check, .hd-radio) { margin: 0; }
.hd-check, .hd-radio {
   appearance: none; -webkit-appearance: none;
   width: 18px; height: 18px;
   flex: 0 0 auto;
   border: 2px solid var(--hd-line-strong);
   background: var(--hd-surface);
   cursor: pointer;
   position: relative;
   transition: background-color .12s ease, border-color .12s ease;
}
.hd-check { border-radius: 5px; }
.hd-radio { border-radius: 50%; }
.hd-check:checked { background: var(--hd-primary); border-color: var(--hd-primary); }
.hd-check:checked::after {
   content: ""; position: absolute;
   left: 4px; top: 1px;
   width: 5px; height: 9px;
   border: solid #fff; border-width: 0 2px 2px 0;
   transform: rotate(45deg);
}
/* A native indeterminate box draws nothing once appearance is off. */
.hd-check:indeterminate { background: var(--hd-primary); border-color: var(--hd-primary); }
.hd-check:indeterminate::after {
   content: ""; position: absolute;
   left: 2px; top: 6px; width: 10px; height: 2px;
   background: #fff; border: 0; transform: none;
}
.hd-radio:checked { border-color: var(--hd-primary); }
.hd-radio:checked::after {
   content: ""; position: absolute;
   left: 4px; top: 4px; width: 6px; height: 6px;
   border-radius: 50%; background: var(--hd-primary);
}
/* Lines the control up with the middle of the first label line in a two-line
   option row, where centring on the whole block would sit it too low and the
   top of the block sits it too high. 18px control on a 24px line. */
.hd-opt-mark { margin-top: 3px; }
.hd-check:disabled, .hd-radio:disabled { cursor: not-allowed; opacity: .5; }
.hd-check:focus-visible, .hd-radio:focus-visible { outline: 2px solid var(--hd-primary); outline-offset: 2px; }

/* ------------------------------------------------------------- switch */
.hd-switch {
   width: 44px; height: 24px; flex: 0 0 auto;
   border: 0; padding: 0;
   border-radius: 12px;
   background: var(--hd-line-strong);
   position: relative; cursor: pointer;
   transition: background-color .15s ease;
}
.hd-switch::after {
   content: ""; position: absolute; top: 2px; left: 2px;
   width: 20px; height: 20px; border-radius: 50%;
   background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
   transition: left .15s ease;
}
.hd-switch[aria-checked="true"], .hd-switch.is-on { background: var(--hd-primary); }
.hd-switch[aria-checked="true"]::after, .hd-switch.is-on::after { left: 22px; }
.hd-switch:focus-visible { outline: 2px solid var(--hd-primary); outline-offset: 2px; }
/* Checkbox-driven form: <input class="sr-only peer"> followed by the switch.
   Both spellings existed at three different sizes; they share one now. */
.peer:checked ~ .hd-switch { background: var(--hd-primary); }
.peer:checked ~ .hd-switch::after { left: 22px; }
.peer:disabled ~ .hd-switch { opacity: .5; cursor: not-allowed; }
.peer:focus-visible ~ .hd-switch { outline: 2px solid var(--hd-primary); outline-offset: 2px; }

/* -------------------------------------------------------- empty state */
/* An empty state is a surface in its own right. Some sit inside a card
   already and some are dropped straight onto the page by a render function,
   which is why they used to look like two different things; the component
   carries the surface so both land the same way. */
.hd-empty {
   padding: 40px 20px;
   text-align: center;
   color: var(--hd-ink-2);
   font-size: .875rem;
   background: var(--hd-surface);
   border: 1px solid var(--hd-line);
   border-radius: var(--hd-r-lg);
}
/* Already on a surface: do not stack a second one. */
.hd-card .hd-empty, .hd-panel .hd-empty, .hd-modal .hd-empty,
td .hd-empty, th .hd-empty, .hd-table .hd-empty {
   background: none; border: 0; border-radius: 0; padding: 40px 20px;
}
.hd-empty-sm, .hd-table .hd-empty-sm, td .hd-empty-sm { padding: 22px 16px; }
.hd-empty .hd-icon { margin: 0 auto 12px; width: 44px; height: 44px; }
.hd-empty .hd-icon > svg { width: 22px; height: 22px; }
.hd-empty-t { font-size: .9375rem; font-weight: 600; color: var(--hd-ink); }
.hd-empty-d { font-size: .875rem; color: var(--hd-ink-2); margin-top: 5px; }
.hd-empty-d + .hd-btn { margin-top: 14px; }

/* -------------------------------------------------------------- modal */
.hd-modal-ov {
   position: fixed; inset: 0; z-index: 50;
   background: rgba(15, 23, 42, .45);
   display: flex; align-items: center; justify-content: center;
   padding: 20px;
}
.hd-modal {
   background: var(--hd-surface);
   border-radius: var(--hd-r-lg);
   box-shadow: 0 20px 50px rgba(0, 0, 0, .25);
   width: 100%; max-width: 480px;
   max-height: calc(100vh - 40px);
   display: flex; flex-direction: column;
}
.hd-modal-hd {
   padding: 18px 20px 0;
   display: flex; align-items: flex-start; gap: 12px;
}
.hd-modal-hd h3 { margin: 0; font-size: 1.0625rem; font-weight: 600; color: var(--hd-ink); }
.hd-modal-hd p { margin: 4px 0 0; font-size: .875rem; color: var(--hd-ink-2); }
.hd-modal-bd { padding: 16px 20px; overflow-y: auto; font-size: .875rem; color: var(--hd-ink); }
.hd-modal-ft {
   padding: 0 20px 18px;
   display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
}

/* --------------------------------------------------------- table rows */
.hd-table { width: 100%; border-collapse: collapse; font-size: .875rem; }
.hd-table th {
   text-align: left; font-size: .875rem; font-weight: 600;
   color: var(--hd-ink-2); padding: 10px 12px;
   border-bottom: 1px solid var(--hd-line);
}
.hd-table td { padding: 12px; border-bottom: 1px solid var(--hd-line); color: var(--hd-ink); }
.hd-table tbody tr:last-child td { border-bottom: 0; }
.hd-table tbody tr:hover { background: var(--hd-surface-2); }

/* ---------------------------------------------------- section heading
   446 headings existed in 31 shapes. Three levels replace them: a page
   title, a card or section header, and a sub-header inside one. */
.hd-h1 { font-size: 1.25rem;   font-weight: 700; letter-spacing: -.01em; margin: 0 0 16px; }
.hd-h2 { font-size: 1.0625rem; font-weight: 600; margin: 0 0 16px; }
.hd-h3 { font-size: .875rem;   font-weight: 600; margin: 0 0 12px; }
/* A heading followed by its own subtitle sits tight to it, and the subtitle
   carries the gap instead, so the block below always clears by the same amount. */
.hd-h1:has(+ p), .hd-h2:has(+ p), .hd-h3:has(+ p) { margin-bottom: 10px; }
/* The subtitle's own top margin is zeroed so a stray mt-1 or mt-2 on one card
   cannot make its gap differ from the next card's. */
.hd-h1 + p, .hd-h2 + p, .hd-h3 + p { margin-top: 0; margin-bottom: 16px; }
/* Inside a heading row (a modal title beside its close, a card title beside a
   control) the wrapper owns the spacing. */
.hd-modal-hd .hd-h1, .hd-modal-hd .hd-h2, .hd-modal-hd .hd-h3,
.hd-panel-hd .hd-h1, .hd-panel-hd .hd-h2, .hd-panel-hd .hd-h3 { margin-bottom: 0; }
/* Same thing for the 44 modal headers built by hand out of a flex row: the
   title's own bottom margin made its side of the row taller, and the close
   button centred on that instead of on the title, sitting visibly low. A title
   that does carry a subtitle keeps its margin, so the pair still reads as one
   block. */
.flex:has([aria-label="Close"]) :is(.hd-h1, .hd-h2, .hd-h3):not(:has(+ p)) { margin-bottom: 0; }
/* With a subtitle the header is two lines tall; the button still belongs beside
   the title, not centred on the pair. */
.flex:has([aria-label="Close"]) > [aria-label="Close"] { align-self: flex-start; }
/* Colour is a default, not a rule: :where() has zero specificity, so a heading
   that carries its own text-white (the branding preview sits on navy) keeps it
   instead of being repainted near-black and disappearing. */
/* In a centred row the heading's bottom margin lifts its text above the icon
   or button beside it. The row carries the spacing instead. */
.flex.items-center > :is(.hd-h1, .hd-h2, .hd-h3) { margin-bottom: 0; }
.flex.items-center:has(> :is(.hd-h1, .hd-h2, .hd-h3)) + p { margin-top: 2px; }
:where(.hd-h1, .hd-h2, .hd-h3) { color: var(--hd-ink); }
.hd-h-sub { font-size: .875rem; color: var(--hd-ink-2); margin: 3px 0 0; }


/* ------------------------------------------------------------ spinner
   25 spinners existed in 15 shapes and 5 hues, including a red one on a
   loading tab (which reads as failure) and a white one on a white card. */
.hd-spin {
   display: inline-block;
   width: 20px; height: 20px;
   border: 2px solid var(--hd-line-strong);
   border-top-color: var(--hd-primary);
   border-radius: 50%;
   animation: hd-spin .7s linear infinite;
}
.hd-spin-sm { width: 14px; height: 14px; }
.hd-spin-lg { width: 32px; height: 32px; border-width: 3px; }
.hd-spin-on-fill { border-color: rgba(255, 255, 255, .35); border-top-color: #fff; }
@keyframes hd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .hd-spin { animation-duration: 2s; } }

/* ------------------------------------------------------------- badge dot
   The unread-count bubble on the alert bell. It used to carry display:flex
   in a style attribute while JavaScript toggled the .hidden class, so the
   red "0" could never be hidden and showed on every page load. */
.hd-dot {
   position: absolute; top: -2px; right: -2px;
   min-width: 22px; height: 22px; padding: 0 5px;
   display: flex; align-items: center; justify-content: center;
   background: var(--hd-danger); color: #fff;
   font-size: .875rem; font-weight: 700; line-height: 1;
   border-radius: 11px;
}

/* -------------------------------------------------------------- menu
   A small pop-up list of actions, anchored under its trigger. */
.hd-menu {
   position: absolute; z-index: 5;
   background: var(--hd-surface); color: var(--hd-ink);
   border: 1px solid var(--hd-line);
   border-radius: var(--hd-r-lg);
   box-shadow: 0 12px 32px rgba(0, 0, 0, .16);
   overflow: hidden;
}
.hd-menu-hd { padding: 14px 16px; border-bottom: 1px solid var(--hd-line); }
.hd-menu-name { font-size: .875rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.hd-menu-sub { font-size: .875rem; color: var(--hd-ink-2); overflow: hidden; text-overflow: ellipsis; }
.hd-menu-item {
   display: flex; align-items: center; gap: 10px;
   width: 100%; padding: 10px 16px;
   font-size: .875rem; text-align: left;
   background: transparent; color: var(--hd-ink); border: 0; cursor: pointer;
}
.hd-menu-item svg { flex: 0 0 auto; color: var(--hd-ink-2); }
.hd-menu-item:hover:not(:disabled) { background: var(--hd-surface-2); }
.hd-menu-item:disabled { opacity: .6; cursor: wait; }
.hd-menu-item-danger { color: var(--hd-danger); border-top: 1px solid var(--hd-line); }
.hd-menu-item-danger svg { color: var(--hd-danger); }

/* -------------------------------------------------------------- chat
   The support chat. Only the floating shell lives
   here. Messages are hd-note-bare strips, the buttons are hd-btn. */
.hd-chat-launch {
   position: fixed; right: 24px; bottom: 24px; z-index: 39;
   border-radius: 9999px;
}
body:has(.hd-chat-launch:not(.hidden)) .main-content { padding-bottom: 72px; }
.hd-chat {
   position: fixed; right: 24px; bottom: 24px; z-index: 40;
   width: min(380px, calc(100vw - 32px));
   height: min(600px, calc(100vh - 48px));
   display: flex; flex-direction: column;
   background: var(--hd-surface); color: var(--hd-ink);
   border: 1px solid var(--hd-line);
   border-radius: var(--hd-r-lg);
   box-shadow: 0 12px 32px rgba(0, 0, 0, .16);
   overflow: hidden;
}
.hd-chat-hd {
   flex: 0 0 auto;
   display: flex; align-items: center; gap: 10px;
   padding: 12px 14px;
   border-bottom: 1px solid var(--hd-line);
}
.hd-chat-hd-t { font-size: .9375rem; font-weight: 600; line-height: 1.3; }
.hd-chat-hd-s { font-size: .8125rem; color: var(--hd-ink-2); line-height: 1.3; }
.hd-chat-bd {
   flex: 1 1 auto;
   overflow-y: auto;
   padding: 14px;
   display: flex; flex-direction: column; gap: 10px;
}
.hd-chat-ft {
   flex: 0 0 auto;
   padding: 10px 12px 12px;
   border-top: 1px solid var(--hd-line);
}
.hd-chat-row { display: flex; align-items: flex-end; gap: 8px; }
.hd-chat-row .hd-textarea { min-height: 43px; max-height: 120px; resize: none; }
.hd-chat-row .hd-btn-icon { flex: 0 0 43px; width: 43px; height: 43px; min-height: 43px; }
.hd-chat-msg { max-width: 86%; align-self: flex-start; overflow-wrap: anywhere; }
.hd-chat-msg.hd-note-quiet { color: var(--hd-ink); }
.hd-chat-me { align-self: flex-end; }
.hd-chat-who { font-size: .75rem; color: var(--hd-ink-2); margin: 0 2px -6px; }
.hd-chat-sys {
   align-self: center; max-width: 92%;
   text-align: center; font-size: .8125rem; line-height: 1.45;
   color: var(--hd-ink-2);
}
.hd-chat-hint { margin-top: 8px; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.hd-chat-hint .hd-hint { margin: 0; text-align: center; }
@media (max-width: 640px) {
   .hd-chat { right: 0; bottom: 0; width: 100vw; height: 100dvh; border: 0; border-radius: 0; z-index: 50; }
   .hd-chat-launch span { display: none; }
   .hd-chat-launch { padding: 14px; }
}

/* ------------------------------------------------------------ hiding
   components.css loads after tailwind.min.css, so a component that sets
   display ties .hidden on specificity and wins on source order. Without
   this block, 47 elements carrying `hidden` would render permanently
   visible. Keep every display-setting component in this list. */
.hd-btn.hidden, .hd-note.hidden, .hd-card.hidden, .hd-panel.hidden,
.hd-panel-hd.hidden, .hd-panel-ctl.hidden, .hd-tile.hidden, .hd-pill.hidden,
.hd-copy.hidden, .hd-copy-btn.hidden, .hd-code.hidden, .hd-icon.hidden,
.hd-empty.hidden, .hd-modal-ov.hidden, .hd-modal.hidden, .hd-modal-hd.hidden,
.hd-modal-ft.hidden, .hd-note-actions.hidden, .hd-switch.hidden,
.hd-label.hidden, .hd-input.hidden, .hd-select.hidden, .hd-textarea.hidden,
.hd-file.hidden, .hd-os.hidden, .hd-avatar.hidden, .hd-check.hidden, .hd-radio.hidden, .hd-empty.hidden, .hd-dot.hidden, .hd-spin.hidden, .hd-input.hidden, .hd-select.hidden, .hd-textarea.hidden, .hd-table.hidden,
.hd-chat.hidden, .hd-chat-msg.hidden, .hd-chat-hint.hidden, .hd-menu-item.hidden, .hd-card-link.hidden { display: none; }

[hidden] { display: none !important; }
