/* TBevel */
/* TBevel — a 3D divider/frame (real VCL TBevel). NO Brush/Pen/border-width: the bevel is entirely the
   Shape x Style classes below (CSS inset/outset/groove/ridge derive their light/dark edges from the border
   colour, giving the classic VCL sunken/raised look). VCL enum values (bsBox/bsFrame/bs*Line/bsSpacer x
   bsLowered/bsRaised) are used as-is (we keep the true VCL prop/enum names). */
.vcl-bevel { box-sizing: border-box; }

/* bsBox — full border. bsLowered = sunken, bsRaised = popped-out. */
.vcl-bevel-bsBox.vcl-bevel-bsLowered     { border: 2px inset  #c0c0c0; }
.vcl-bevel-bsBox.vcl-bevel-bsRaised      { border: 2px outset #c0c0c0; }
/* bsFrame — a double-bevelled groove/ridge. */
.vcl-bevel-bsFrame.vcl-bevel-bsLowered   { border: 2px groove #c0c0c0; }
.vcl-bevel-bsFrame.vcl-bevel-bsRaised    { border: 2px ridge  #c0c0c0; }
/* single-edge lines */
.vcl-bevel-bsTopLine.vcl-bevel-bsLowered     { border-top: 2px groove #c0c0c0; }
.vcl-bevel-bsTopLine.vcl-bevel-bsRaised      { border-top: 2px ridge  #c0c0c0; }
.vcl-bevel-bsBottomLine.vcl-bevel-bsLowered  { border-bottom: 2px groove #c0c0c0; }
.vcl-bevel-bsBottomLine.vcl-bevel-bsRaised   { border-bottom: 2px ridge  #c0c0c0; }
.vcl-bevel-bsLeftLine.vcl-bevel-bsLowered    { border-left: 2px groove #c0c0c0; }
.vcl-bevel-bsLeftLine.vcl-bevel-bsRaised     { border-left: 2px ridge  #c0c0c0; }
.vcl-bevel-bsRightLine.vcl-bevel-bsLowered   { border-right: 2px groove #c0c0c0; }
.vcl-bevel-bsRightLine.vcl-bevel-bsRaised    { border-right: 2px ridge  #c0c0c0; }
/* bsSpacer — invisible spacer (no border) */

/* TBitBtn */
/* TBitBtn — a push button with an optional bitmap glyph + a Kind preset (real VCL TBitBtn, parent TCustomButton).
   Renders its Caption; geometry/Font from the inline {Style}. Kind (bkOK/bkCancel/...), Default, Cancel, Glyph are
   carried as real VCL props (the standard Kind glyph is later polish). VCL prop names kept verbatim. */
.vcl-bitbtn {
  cursor: pointer;
  border: 1px solid #adadad;
  border-radius: 3px;
  background: linear-gradient(to bottom, #f5f5f5, #dcdcdc);
  color: #1a1a1a;
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-bitbtn:hover  { background: linear-gradient(to bottom, #e8f0fe, #c2d4f8); border-color: #4a7abf; }
.vcl-bitbtn:active { background: linear-gradient(to bottom, #c2d4f8, #e8f0fe); }

/* TCheckListBox */
/* TCheckListBox — a scrolling list whose rows each carry a checkbox. Not a <select>: TListBox can be one
   because a row is just text, but a checkable row needs a real <input> per line, so this is a <div> of
   <label> rows. Row height comes from the model's ItemHeight (emitted inline per row by {ItemsChecks}),
   which is why nothing here sets one. */
.vcl-checklistbox {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* fallback — {Style} FontCss overrides when the model sets font */
  color: #000;
  padding: 0;
  overflow-y: auto;
}
.vcl-checklistbox > label {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 2px;
  white-space: nowrap;
  cursor: default;
}
.vcl-checklistbox > label > input {
  margin: 0;
  flex: 0 0 auto;
  width: 1em !important;
  height: 1em !important;
}

/* TColorBox */
/* TColorBox — a color-picker combobox (real VCL TColorBox, parent TCustomComboBox). Renders a closed combobox: a
   swatch of the Selected colour (painted via the --sel CSS var set by ColorVarCss in compPackageUnit) + the colour
   name + a drop arrow. Style (the cb* set of which colour groups appear) is carried as a real VCL prop. VCL names kept. */
.vcl-colorbox {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 4px;
  background: #fff;
  border: 1px solid rgb(173, 173, 173);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-colorbox-sw    { width: 14px; height: 14px; border: 1px solid #888; background: var(--sel, #000); flex: 0 0 auto; }
.vcl-colorbox-cap   { flex: 1 1 auto; overflow: hidden; white-space: nowrap; }
.vcl-colorbox-arrow { flex: 0 0 auto; color: #555; }

/* TColorListBox */
/* TColorListBox — a list of colour swatches (real VCL TColorListBox, parent TCustomListBox). Design-time preview: the
   Selected colour row (swatch via the --sel CSS var from ColorVarCss) plus a few representative standard colours.
   Style (the cb* set) is carried as a real VCL prop; the live colour set is a runtime concern. VCL names kept. */
.vcl-colorlistbox {
  background: #fff;
  border: 1px solid rgb(173, 173, 173);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  overflow: hidden;
}
.vcl-clb-row    { display: flex; align-items: center; gap: 4px; padding: 1px 3px; height: 16px; }
.vcl-clb-sel    { background: #3399ff; color: #fff; }
.vcl-clb-row .vcl-swatch { width: 12px; height: 12px; border: 1px solid #888; flex: 0 0 auto; }

/* TFlexPanel */
.vcl-flexpanel {
  box-sizing: border-box;
  display: flex;
}

/* TFlowPanel */
/* TFlowPanel — a real VCL TFlowPanel: a panel that AUTO-FLOWS its children. We map its real VCL FlowStyle/AutoWrap
   onto the granted flex layout exception in LayoutCssFromDfm (compPackageUnit) BY CLASS (display:flex + direction +
   wrap), so children become flex items (FlexItemStyle). The background/border is the panel face; geometry comes from
   the inline {Style}. VCL prop/enum names kept verbatim (FlowStyle fs*, AutoWrap). */
.vcl-flowpanel {
  background: #f0f0f0;
  border: 1px solid #d0d0d0;
  box-sizing: border-box;
}

/* TGridPanel */
/* TGridPanel — a real VCL TGridPanel: a panel that lays children in a grid (ColumnCollection x RowCollection).
   We map its real VCL ColumnCount/RowCount onto the granted grid layout exception in LayoutCssFromDfm (compPackageUnit)
   BY CLASS (display:grid + grid-template-columns/rows: repeat(N,1fr)), so children become grid items (GridItemStyle,
   auto-placed cell by cell). Geometry comes from the inline {Style}. VCL prop names kept verbatim. */
.vcl-gridpanel {
  background: #f0f0f0;
  border: 1px solid #d0d0d0;
  box-sizing: border-box;
}

/* TImage */
/* TImage — a real VCL TImage. The Picture (a base64 'data:image/png;base64,...' URI held in the Picture prop) paints
   as a CSS background-image via the inline {Style} (ImageCssFromDfm); Stretch/Proportional/Center map to
   background-size/position. No backdrop fill — like the VCL control (and the reference <img>) it is transparent; an
   empty Picture shows a dashed placeholder (emitted inline by ImageCssFromDfm). VCL prop/enum names kept verbatim
   (stay true VCL — Picture/Stretch/Proportional/Center/AutoSize/Transparent). */
.vcl-image { box-sizing: border-box; background-repeat: no-repeat; background-position: center; }

/* TMaskEdit */
/* TMaskEdit — a masked text edit (real VCL TMaskEdit, parent TCustomMaskEdit). Renders an <input> showing Text; the
   EditMask is surfaced as the placeholder hint. MaxLength/ReadOnly/BorderStyle are carried as real VCL props. VCL
   prop names kept verbatim. */
.vcl-maskedit {
  background: #fff;
  border: 1px solid rgb(173, 173, 173);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}

/* TPaintBox */
/* TPaintBox — a blank owner-drawn canvas (real VCL TPaintBox, parent TGraphicControl). It has no content of its own
   (the app paints it at runtime via OnPaint), so at design time we show an empty box with a faint hatch + border so
   the surface is visible/selectable. Geometry/Font come from the inline {Style}. VCL prop names kept verbatim. */
.vcl-paintbox {
  box-sizing: border-box;
  border: 1px solid #c0c0c0;
  background: repeating-linear-gradient(45deg, transparent, transparent 6px, rgba(0,0,0,0.04) 6px, rgba(0,0,0,0.04) 7px);
}

/* TShape */
/* TShape — a styled box (real VCL TShape). The fill (Brush.Color) + outline (Pen.Color/Width/Style) come from the
   inline {Style} (ShapeCssFromDfm); the Shape prop only picks the corner rounding via the class below. VCL enum
   values (st*) kept as-is (stay on true VCL). */
.vcl-shape { box-sizing: border-box; }
.vcl-shape-stRoundRect,
.vcl-shape-stRoundSquare { border-radius: 8px; }
.vcl-shape-stEllipse,
.vcl-shape-stCircle      { border-radius: 50%; }
/* stRectangle / stSquare: square corners (no radius) */

/* TSpeedButton */
/* TSpeedButton — a compact flat/group button (real VCL TSpeedButton, parent TGraphicControl). Renders its Caption;
   geometry/Font come from the inline {Style}. Flat/Down/GroupIndex/AllowAllUp are carried as real VCL props (visual
   pressed/flat states are later polish). VCL prop names kept verbatim (stay true VCL). */
.vcl-speedbutton {
  cursor: pointer;
  border: 1px solid #adadad;
  border-radius: 2px;
  background: linear-gradient(to bottom, #f5f5f5, #e4e4e4);
  color: #1a1a1a;
  font: 11px Tahoma, sans-serif;
  padding: 0;
  box-sizing: border-box;
}
.vcl-speedbutton:hover  { background: linear-gradient(to bottom, #e8f0fe, #c2d4f8); border-color: #4a7abf; }
.vcl-speedbutton:active { background: linear-gradient(to bottom, #c2d4f8, #e8f0fe); }
/* v1047: Flat = True -- no frame and no face until the pointer is over it, as the VCL's flat speed button */
.vcl-speedbutton[data-tp-flat] { border-color: transparent; background: transparent; }
.vcl-speedbutton[data-tp-flat]:hover { border-color: #adadad; background: linear-gradient(to bottom, #f5f5f5, #e4e4e4); }

/* TSpinEdit */
/* TSpinEdit — a number field with the browser's own spin buttons. The geometry mirror sizes the <input>
   directly (it IS the data-tp-name host), so width/height come from {Style}; everything here is the VCL
   look: a sunken 1px border on white, Tahoma, and the same box-sizing rule TEdit uses so a designed width
   is the OUTER width. The up/down arrows are the platform's, deliberately — TSpinEdit's buttons are the
   OS's on Windows too, so borrowing the browser's keeps the parity without shipping two arrow glyphs. */
.vcl-spinedit {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* fallback — {Style} FontCss overrides when the model sets font */
  color: #000;
  padding: 1px 2px;
}

/* TSplitter */
/* TSplitter — a draggable pane divider (real VCL TSplitter, parent TCustomControl). A thin bar painted in the panel
   face colour with a resize cursor. Align/MinSize/ResizeStyle/Beveled are carried as real VCL props (the actual drag
   resize is a runtime concern). VCL prop names kept verbatim. */
.vcl-splitter {
  background: #f0f0f0;
  box-sizing: border-box;
  cursor: col-resize;
}

/* TStringGrid */
/* TStringGrid — a fixed-header cell grid. The <table> IS the geometry host, so {Style} sizes it and the
   cells fall inside; `table-layout:fixed` makes DefaultColWidth actually govern (an auto table would
   redistribute columns by content and ignore it). Fixed cells get the .fx class from {GridCells}, matching
   the VCL's FixedColor band across the top row and down the left column. */
/* *** v751: THE GRID IS CLIPPED BY A HOST, BECAUSE A <table> CANNOT BE. ***
   CSS height on a table is a MINIMUM, not a maximum, and `overflow` on a table
   box does nothing -- so a grid with more rows than fit simply grew past its own
   rectangle. MEASURED on the SQLite table designer: gridCols is Height = 232 in
   the .dfm, style="height:232px" was emitted correctly, and the element rendered
   287px because thirteen rows said so -- spilling over the "Indexes, one per
   line" label underneath it. The VCL clips and scrolls; now so does this.
   The HOST carries the .dfm geometry, the border and the fill; the table keeps
   table-layout:fixed so DefaultColWidth still governs the columns. */
.vcl-stringgrid-host {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  color: #000;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* fallback -- {Style} FontCss overrides when the model sets font */
  overflow: auto;
}
.vcl-stringgrid {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
  font: inherit;
  color: inherit;
}
.vcl-stringgrid td {
  border: 1px solid #c0c0c0;
  padding: 1px 3px;
  overflow: hidden;
  white-space: nowrap;
}
.vcl-stringgrid td.fx {
  background: #f0f0f0;
  font-weight: normal;
}

/* TYouTube */
/* TYouTube — host carries the geometry (inline {Style}). On /design (no client.js) the dark
   placeholder below is shown (the HTML analogue of the reference's GDI preview panel: dark box +
   red play button + "YouTube" + the video id). At runtime client.js replaces the inner content with
   the actual <iframe> embed. */

.vcl-youtube {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  background: #1a1a1a;
  border: 0;
}
.vcl-youtube > iframe { display: block; width: 100%; height: 100%; border: 0; }

/* design-time placeholder */
.vcl-youtube-ph {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: Tahoma, "Segoe UI", sans-serif;
  user-select: none;
}
.vcl-youtube-play { display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,.4)); }
.vcl-youtube-brand { color: #ffffff; font-size: 13px; font-weight: bold; letter-spacing: .3px; }
.vcl-youtube-id { color: #aaaaaa; font-size: 11px; max-width: 92%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.vcl-youtube-id:empty::before { content: "(no video)"; color: #777777; }

/* TAudioPlayer */
/* TAudioPlayer — verbatim from the reference styles/classic.css (.vcl-audio*). A card with a hidden
   <audio> + a custom UI: title + clock, play button, seek timeline, mute/loop toggles, volume slider.
   client.js (ported wireAudioPlayer) drives playback + updates the timeline/labels and fires events. */

.vcl-audio {
  box-sizing: border-box; display: flex; flex-direction: column; justify-content: center;
  gap: 6px; padding: 8px 10px; background: #f5f6f8; border: 1px solid #c2c7d0; border-radius: 4px;
  font: 12px Tahoma, sans-serif; color: #333; -webkit-user-select: none; user-select: none;
}
.vcl-audio-el { display: none; }
.vcl-audio-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.vcl-audio-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-weight: bold; }
.vcl-audio-clock { flex: 0 0 auto; color: #666; white-space: nowrap; font-variant-numeric: tabular-nums; }
.vcl-audio-dur { cursor: pointer; }   /* click toggles total <-> remaining */
.vcl-audio-row { display: flex; align-items: center; gap: 8px; }
.vcl-audio-play {
  flex: 0 0 auto; width: 26px; height: 26px; padding: 0; box-sizing: border-box;
  border: 1px solid #8a90a0; border-radius: 50%; background: #fff; color: #333; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 12px; line-height: 1;
}
.vcl-audio-play:hover { background: #eef1f6; }
.vcl-audio-play::before { content: '\25B6'; }                      /* play triangle */
.vcl-audio--playing .vcl-audio-play::before { content: '\23F8'; }  /* pause bars */
.vcl-audio-seek {
  position: relative; flex: 1 1 auto; height: 16px; cursor: pointer; touch-action: none; outline: none;
}
.vcl-audio-seek:focus-visible { outline: 1px dotted #888; }
.vcl-audio-groove { position: absolute; left: 7px; right: 7px; top: 50%; height: 4px;
  transform: translateY(-50%); background: #d9dde4; border: 1px solid #b0b6c0; box-sizing: border-box; }
.vcl-audio-fill { position: absolute; left: 7px; top: 50%; height: 4px; width: 0;
  transform: translateY(-50%); background: #3b82f6; }
.vcl-audio-thumb { position: absolute; top: 50%; left: 7px; width: 10px; height: 14px;
  transform: translate(-50%, -50%); background: #fff; border: 1px solid #5a6270; border-radius: 2px; }
.vcl-audio-mute, .vcl-audio-loop {
  flex: 0 0 auto; width: 24px; height: 22px; padding: 0; box-sizing: border-box;
  border: 1px solid #b0b6c0; border-radius: 3px; background: #fff; color: #444; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 12px; line-height: 1;
}
.vcl-audio-mute:hover, .vcl-audio-loop:hover { background: #eef1f6; }
.vcl-audio-mute::before { content: '\1F50A'; }                    /* speaker */
.vcl-audio--muted .vcl-audio-mute::before { content: '\1F507'; }  /* muted speaker */
.vcl-audio-loop::before { content: '\1F501'; }                    /* repeat */
.vcl-audio--loop .vcl-audio-loop { background: #e7f0ff; border-color: #3b82f6; color: #1d4ed8; }
.vcl-audio-vol { position: relative; flex: 0 0 56px; height: 16px; cursor: pointer; touch-action: none; outline: none; }
.vcl-audio-vol:focus-visible { outline: 1px dotted #888; }

/* TContentSlot */
/* TContentSlot — a DESIGN-TIME marker only. It shows where a page's own body is
   spliced in when the page uses this form as its layout. It is REMOVED from the
   composed page at render/export time (formRendererUnit.ComposePageForm), so the
   running app never paints it — these styles are seen only on the layout's
   design canvas. */
.vcl-contentslot{
  box-sizing:border-box;
  display:flex; align-items:center; justify-content:center;
  border:2px dashed #9aa7bd; border-radius:8px;
  background:repeating-linear-gradient(45deg,#f5f7fb,#f5f7fb 12px,#eef2f8 12px,#eef2f8 24px);
  color:#5b6b86; font-family:Tahoma,sans-serif; overflow:hidden;
}
.vcl-contentslot .tp-slot-marker{ text-align:center; pointer-events:none; }
.vcl-contentslot .tp-slot-title{ font-size:15px; font-weight:600; letter-spacing:.3px; }
.vcl-contentslot .tp-slot-sub{ font-size:11px; opacity:.7; margin-top:4px; }
/* v476: design-time placeholder text moved out of template.html (it used to ship into every exported
   page) into CSS-generated content scoped to the design canvas. See TScrollStory/styles.css. */
.tp-design .vcl-contentslot .tp-slot-title::before{ content:'Page content'; }
.tp-design .vcl-contentslot .tp-slot-sub::before{ content:'each page using this layout renders its own body here'; }

/* TFileDrop */
.vcl-filedrop {
  box-sizing: border-box;
  border: 2px dashed #a0a0a0;
  background: #fafafa;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #808080;
  font: 12px Tahoma, sans-serif;
  text-align: center;
}

/* TFooter */
.vcl-footer {
  box-sizing: border-box;
  border-top: 1px solid #d0d0d0;
  background: #f7f7f7;
  display: flex;
  align-items: center;
  padding: 4px 8px;
  font: 11px Tahoma, sans-serif;
  color: #444;
}

/* TGemini */
/* TGemini is a NON-VISUAL component: it uses the shared .vcl-nonvisual puck (display:none at runtime,
   revealed at design by ServeDesign; the instance name is the ::after label — both rules come from
   components/Data/TMySQLDatabase/styles.css via /components.css). Here we override only the puck GLYPH
   with a Gemini "spark" badge. The element still exists (hidden) at runtime so SendPrompt can target it. */

.vcl-nonvisual.vcl-gemini {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect x='.5' y='.5' width='23' height='23' rx='5' fill='%234285F4'/%3E%3Cpath d='M12 4c.4 3.4 1.2 4.2 4.6 4.6C13.2 9 12.4 9.8 12 13.2 11.6 9.8 10.8 9 7.4 8.6 10.8 8.2 11.6 7.4 12 4z' fill='%23ffffff'/%3E%3Cpath d='M16.5 13c.2 1.7.6 2.1 2.3 2.3-1.7.2-2.1.6-2.3 2.3-.2-1.7-.6-2.1-2.3-2.3 1.7-.2 2.1-.6 2.3-2.3z' fill='%23ffffff' opacity='.85'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
}

/* THeader */
/* THeader (W) -- .vcl-header bar chrome. client.js builds the DOM + inline-styles the .vcl-popup-menu
   dropdowns (appended to <body>) + toggles the collapse classes. Runtime only (hidden at /design). */

/* v376 (port tsw2 v305): chrome colour literals flow through --tp-* theme tokens (fallbacks = pre-v376
   literals -> classic pixel-identical). The comp's own --hdr-* runtime vars (set inline by client.js from
   props) and the semantic error red are left as-is. */
.vcl-header { box-sizing:border-box; display:flex; align-items:center; gap:12px; width:100%; min-height:var(--hdr-h,64px); padding:0 16px; background:var(--tp-surface-2,#f0f0f0); border-bottom:1px solid var(--tp-border,#888); font:12px Tahoma,sans-serif; color:var(--tp-fg,#333); overflow:hidden; -webkit-user-select:none; user-select:none; }
.vcl-header--sticky { position:sticky; top:0; z-index:50; }
.vcl-header--noborder { border-bottom:none; }
.vcl-header--menu-left { gap:0; }
.vcl-header--menu-left .vcl-header-menu { margin-left:35px; }
.vcl-header-brand { display:flex; align-items:center; gap:10px; flex:0 0 auto; min-width:0; }
.vcl-header-logo { max-height:calc(var(--hdr-h,64px) - 20px); max-width:220px; height:auto; width:auto; object-fit:contain; display:block; }
.vcl-header-text { display:flex; flex-direction:column; justify-content:center; line-height:1.15; min-width:0; }
.vcl-header-title { font-weight:bold; font-size:1.25em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.vcl-header-subtitle { font-size:0.85em; opacity:0.72; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.vcl-header-menu { display:flex; align-items:stretch; gap:2px; margin-left:auto; flex:0 1 auto; min-width:0; overflow:hidden; }
.vcl-header-mi { padding:3px 12px 6px; display:flex; align-items:center; cursor:pointer; white-space:nowrap; flex:0 0 auto; }  /* v401 (tsw2 v331): taller hover-highlight box (+3px top / +6px bottom) */
.vcl-header-mi:hover { background:var(--tp-hover,rgba(0,0,0,0.06)); }
/* v445 (tsw2 v375): active-nav marker -- an underline bar on the item whose url IS the current page ("you are here").
   inset box-shadow = zero layout shift (a border would change the item box); currentColor follows the
   menu font color, so it adapts to themes and the invert-hover variant with no extra rules. */
.vcl-header-mi--current { box-shadow:inset 0 -2px 0 currentColor; }
.vcl-header-mi--disabled { color:var(--tp-fg-muted,#999); cursor:default; }
.vcl-header-mi--disabled:hover { background:none; }
.vcl-header--invert-sel .vcl-header-menu .vcl-header-mi:not(.vcl-header-mi--disabled):hover { background:var(--hdr-menu-color,currentColor); color:var(--hdr-bg,#f0f0f0); }
.vcl-header--banner .vcl-header-title, .vcl-header--banner .vcl-header-subtitle { text-shadow:0 1px 2px rgba(0,0,0,0.45); }

.vcl-header-btns { display:flex; align-items:stretch; flex:0 0 auto; }
.vcl-header-btn { display:flex; align-items:center; justify-content:center; box-sizing:border-box; width:30px; align-self:stretch; padding:0 5px; background:none; border:none; cursor:pointer; color:inherit; flex:0 0 auto; }
.vcl-header-btn:hover { background:var(--tp-hover,rgba(0,0,0,0.06)); }
.vcl-header-btn svg { width:18px; height:18px; display:block; }
.vcl-header--collapsed .vcl-header-btns { display:none; }

.vcl-header-burger { display:none; box-sizing:border-box; width:34px; height:26px; padding:5px 6px; flex-direction:column; justify-content:space-between; align-items:stretch; background:none; border:none; cursor:pointer; color:inherit; flex:0 0 auto; }
.vcl-header-burger span { display:block; height:2px; background:currentColor; border-radius:1px; }
.vcl-header--collapsed .vcl-header-menu { display:none; }
.vcl-header--collapsed .vcl-header-burger { display:flex; position:absolute; left:16px; top:50%; transform:translateY(-50%); }
/* v448 (port tpwebide/styles/classic.css:1612-1615, rule #2): on collapse the burger goes position:absolute at
   left:16px -- OUT of the flex flow -- but the in-flow brand still began at the header's own padding-left:16px, so
   the burger painted straight on top of the title. The reference CENTERS the brand when collapsed (burger owns the
   left inset, login the right), which clears it; W had never ported these two rules.
   position:relative makes the HEADER the burger's containing block. Without it the absolute burger resolves against
   the nearest positioned ancestor -- the form body is position:relative -- and escapes the header entirely (its
   top:50% would centre it on the whole FORM). Sticky is already a containing block, so don't override its position:
   the reference's exact :not() guard.
   OMITTED from the reference block, per "adapt only what our backend forces": its TopMenu variants
   (.vcl-header--has-topmenu / .vcl-header-row2 / --hdr-base-h) -- W dropped TopMenu at v234 ("main hierarchical Menu
   only") and emits neither class, so the reference's :not(.vcl-header--has-topmenu) qualifier is always true here and
   is folded away. Its `.vcl-header-brand { margin-left/right:0 !important }` reset is omitted too: that exists to
   defeat the reference's INLINE lpPage/tpPage page-inset margin on the brand, and W's client.js brand() sets no
   inline margin (--hdr-page-inset is READ by the login only, never set anywhere in W). */
.vcl-header--collapsed { justify-content:center; }
.vcl-header--collapsed:not(.vcl-header--sticky) { position:relative; }

/* v367 (tsw2 v296): the login widget -- bar button (icon; name+icon when signed in) + the sign-in panel
   (appended to <body>). The two tsw2 rules coupling login to the SEARCH button are omitted (W has no search). */
.vcl-header-login { display:flex; align-items:center; justify-content:center; box-sizing:border-box; width:34px; align-self:stretch; padding:0 6px; background:none; border:none; cursor:pointer; color:inherit; flex:0 0 auto; margin-right:var(--hdr-page-inset,0px); }
.vcl-header-login:hover { background:var(--tp-hover,rgba(0,0,0,0.06)); }
.vcl-header-login svg { width:18px; height:18px; display:block; }
.vcl-header-login--auth { gap:6px; width:auto; padding:0 10px; }
.vcl-header-login-name { font-size:0.92em; white-space:nowrap; max-width:140px; overflow:hidden; text-overflow:ellipsis; }
.vcl-header--menu-left.vcl-header--has-login:not(.vcl-header--has-btns) .vcl-header-login { margin-left:auto; }
.vcl-header--collapsed .vcl-header-login { position:absolute; right:calc(16px + var(--hdr-page-inset,0px)); top:50%; transform:translateY(-50%); align-self:auto; margin-right:0; width:34px; padding:0 6px; gap:0; }
.vcl-header--collapsed .vcl-header-login-name { display:none; }

.vcl-header-login-panel { position:fixed; z-index:9999; background:var(--tp-surface,#fff); border:1px solid var(--tp-border,#c0c0c0); box-shadow:0 4px 14px rgba(0,0,0,0.18); border-radius:4px; padding:10px; display:flex; flex-direction:column; gap:6px; width:220px; }
.vcl-header-login-panel input { font:13px Tahoma,sans-serif; padding:5px 8px; border:1px solid var(--tp-input-border,#c0c0c0); border-radius:3px; outline:none; color:var(--tp-fg,#222); background:var(--tp-input-bg,#fff); }
.vcl-header-login-panel input:focus { border-color:#5b9dd9; }
.vcl-header-login-panel button { font:13px Tahoma,sans-serif; padding:5px 10px; border:1px solid var(--tp-input-border,#b5b5b5); border-radius:3px; background:var(--tp-btn-bg,#f3f3f3); cursor:pointer; color:var(--tp-fg,#222); }
.vcl-header-login-panel button:hover { background:var(--tp-hover,#e6e6e6); }
.vcl-header-login-err { color:#c0392b; font-size:0.85em; min-height:0; }
/* v404 (tsw2 v334): the sign-in <-> create-account toggle link under the login panel. W built the toggle
   DOM (client.js) in an earlier port but omitted this CSS block (tsw2 has carried it since v326), so W's
   toggle link had been inheriting the body font (too big) -- exactly the defect v334 fixes. Ported verbatim
   from tsw2 v334, landed at the v334 value (font 13px = the Sign in / Create account button, not the pre-v334 11px). */
.vcl-header-login-toggle { text-align: center; margin-top: 2px; }
.vcl-header-login-toggle a { font: 13px Tahoma, sans-serif; color: var(--tp-accent, #2563eb); text-decoration: none; cursor: pointer; }
.vcl-header-login-toggle a:hover { text-decoration: underline; }

/* v403 (tsw2 v333): public @username + avatar. The header shows a small round avatar thumbnail in the account
   button when a user has one. (v404 (tsw2 v334): the signup avatar PICKER was removed -- avatar upload will
   live on the user profile page; the thumbnail display + the "check your email" message stay.) */
.vcl-header-avatar {
  width: 20px; height: 20px; border-radius: 50%; object-fit: cover;
  display: block; flex: 0 0 auto; border: 1px solid var(--tp-border, #c0c0c0);
}
.vcl-header-login-msg {
  font: 13px Tahoma, sans-serif; color: var(--tp-fg, #1f2937);
  background: var(--tp-surface-2, #f0fdf4); border: 1px solid #86efac; border-radius: 4px;
  padding: 10px 12px; text-align: center; line-height: 1.4;
}

/* v405 (tsw2 v335): the "My profile" modal (avatar / display name / @username / change password) */
.vcl-header-modal-overlay {
  position: fixed; inset: 0; z-index: 10000; background: rgba(0, 0, 0, 0.42);
  display: flex; align-items: flex-start; justify-content: center; padding: 8vh 16px 16px;
  font: 13px Tahoma, sans-serif;
}
.vcl-header-modal {
  width: 360px; max-width: 100%; background: var(--tp-surface, #fff); color: var(--tp-fg, #222);
  border: 1px solid var(--tp-border, #c0c0c0); border-radius: 6px; box-shadow: 0 10px 34px rgba(0, 0, 0, 0.28);
  max-height: 84vh; display: flex; flex-direction: column; overflow: hidden;
}
.vcl-header-modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--tp-border, #e5e7eb);
}
.vcl-header-modal-title { font-size: 15px; font-weight: 600; }
.vcl-header-modal-x {
  border: 0; background: transparent; cursor: pointer; font-size: 22px; line-height: 1;
  color: var(--tp-fg-muted, #6b7280); padding: 0 4px;
}
.vcl-header-modal-x:hover { color: var(--tp-fg, #222); }
.vcl-header-modal-body { padding: 14px; display: flex; flex-direction: column; gap: 12px; overflow-y: auto; }
.vcl-header-modal input {
  font: 13px Tahoma, sans-serif; padding: 6px 8px; border: 1px solid var(--tp-input-border, #c0c0c0);
  border-radius: 3px; outline: none; color: var(--tp-fg, #222); background: var(--tp-input-bg, #fff); width: 100%; box-sizing: border-box;
}
.vcl-header-modal input:focus { border-color: #5b9dd9; }
.vcl-header-prof-ro { background: var(--tp-surface-2, #f3f4f6) !important; color: var(--tp-fg-muted, #6b7280) !important; cursor: not-allowed; }
.vcl-header-prof-avatar-row { display: flex; align-items: center; gap: 14px; }
.vcl-header-prof-avatar {
  width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;
  background: var(--tp-surface-2, #eef0f2) center/cover no-repeat; border: 1px solid var(--tp-border, #c0c0c0);
}
.vcl-header-prof-avatar.is-empty { border-style: dashed; }
.vcl-header-prof-avatar-btns { display: flex; flex-direction: column; gap: 6px; }
.vcl-header-prof-field { display: flex; flex-direction: column; gap: 4px; }
.vcl-header-prof-label { font-size: 11px; color: var(--tp-fg-muted, #6b7280); }
.vcl-header-prof-btn2 {
  font: 12px Tahoma, sans-serif; padding: 5px 12px; border: 1px solid var(--tp-input-border, #c0c0c0);
  border-radius: 3px; background: var(--tp-btn-bg, #f3f3f3); cursor: pointer; color: var(--tp-fg, #222);
}
.vcl-header-prof-btn2:hover { background: var(--tp-hover, #e6e6e6); }
.vcl-header-prof-btn2--rm { color: #b91c1c; }
.vcl-header-prof-pwtoggle {
  align-self: flex-start; border: 0; background: transparent; cursor: pointer; padding: 0;
  font: 13px Tahoma, sans-serif; color: var(--tp-accent, #2563eb); text-decoration: none;
}
.vcl-header-prof-pwtoggle:hover { text-decoration: underline; }
.vcl-header-prof-pwtoggle.is-open { font-weight: 600; }
.vcl-header-prof-pw { display: flex; flex-direction: column; gap: 8px; }
.vcl-header-prof-msg { font-size: 12px; line-height: 1.4; min-height: 0; }
.vcl-header-prof-msg.is-err { color: #c0392b; }
.vcl-header-prof-msg.is-ok { color: #15803d; }
.vcl-header-prof-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 2px; }
.vcl-header-prof-save {
  font: 13px Tahoma, sans-serif; padding: 6px 14px; border: 1px solid var(--tp-accent, #2563eb); border-radius: 3px;
  background: var(--tp-accent, #2563eb); color: #fff; cursor: pointer;
}
.vcl-header-prof-save:hover { filter: brightness(1.06); }
.vcl-header-prof-save:disabled { opacity: 0.6; cursor: default; }

/* v406 (tsw2 v336): the "My license" modal (reuses the .vcl-header-modal* shell) */
.vcl-header-lic-empty { font-size: 13px; color: var(--tp-fg-muted, #6b7280); }
.vcl-header-lic-cta { align-self: flex-start; font: 13px Tahoma, sans-serif; color: var(--tp-accent, #2563eb); text-decoration: none; }
.vcl-header-lic-cta:hover { text-decoration: underline; }
.vcl-header-lic-card {
  border: 1px solid var(--tp-border, #e5e7eb); border-radius: 8px; padding: 14px;
  display: flex; flex-direction: column; gap: 8px; background: var(--tp-surface-2, #f9fafb);
}
.vcl-header-lic-card + .vcl-header-lic-card { margin-top: 4px; }
.vcl-header-lic-top { display: flex; align-items: center; gap: 8px; }
.vcl-header-lic-badge {
  display: inline-block; font-size: 12px; font-weight: bold; padding: 4px 12px; border-radius: 20px;
  background: #eef6ee; color: #15803d; border: 1px solid #cfe8cf;
}
.vcl-header-lic-status { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--tp-fg-muted, #6b7280); }
.vcl-header-lic-status.is-active { color: #15803d; }
.vcl-header-lic-status.is-expired, .vcl-header-lic-status.is-revoked { color: #c0392b; }
.vcl-header-lic-prod { font-size: 13px; font-weight: 600; color: var(--tp-fg, #222); }
.vcl-header-lic-label { font-size: 11px; color: var(--tp-fg-muted, #6b7280); }
.vcl-header-lic-keyrow { display: flex; gap: 8px; align-items: center; }
.vcl-header-lic-key {
  flex: 1; font-family: Consolas, 'Courier New', monospace; font-size: 13px; letter-spacing: .5px;
  background: var(--tp-surface, #fff); border: 1px solid var(--tp-border, #e5e7eb); border-radius: 4px;
  padding: 8px 10px; color: var(--tp-fg, #222); word-break: break-all;
}
.vcl-header-lic-copy {
  font: 12px Tahoma, sans-serif; padding: 8px 12px; border: 1px solid var(--tp-input-border, #c0c0c0);
  border-radius: 4px; background: var(--tp-btn-bg, #f3f3f3); cursor: pointer; color: var(--tp-fg, #222); white-space: nowrap;
}
.vcl-header-lic-copy:hover { background: var(--tp-hover, #e6e6e6); }
.vcl-header-lic-meta { font-size: 12px; color: var(--tp-fg-muted, #6b7280); }

/* v483: the language picker. Cloned from .vcl-header-login so it shares the header's 34px stretch box and
   hover, then given a text badge instead of an svg. */
.vcl-header-lang { display:flex; align-items:center; justify-content:center; box-sizing:border-box; min-width:34px; align-self:stretch; padding:0 8px; background:none; border:none; cursor:pointer; color:inherit; flex:0 0 auto; }
.vcl-header-lang:hover { background:var(--tp-hover,rgba(0,0,0,0.06)); }
.vcl-header-lang-code { font-size:12px; font-weight:600; letter-spacing:.4px; line-height:1; }

/* COLLAPSED STATE -- a deliberate decision, not a clone. Collapsed, the burger is position:absolute left:16px
   and the login pins right: a TWO-ANCHOR layout with no third slot. Dropping a third stretch box in there
   overlaps one of them. So the picker is hidden and its entries are appended to the burger popup instead
   (see langPick + the burger's own renderPopupMenu call). */
.vcl-header--collapsed .vcl-header-lang { display:none; }

/* THTMLMemo */
.vcl-htmlmemo {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 12px Arial, sans-serif;
  overflow: auto;
  padding: 4px;
  color: #000;
}

/* THTMLText */
.vcl-htmltext {
  box-sizing: border-box;
  font: 12px Arial, sans-serif;
  overflow: auto;
  color: #000;
}
.vcl-htmltext:empty::before {
  content: 'HTML';
  color: #b0b0b0;
  font-style: italic;
}

/* TOutlineButton */
/* TOutlineButton -- a flat button whose look IS its outline. The three Outline props land as
   inline CSS vars (manifest cssVars -> {Style}): --ob-c color, --ob-w px, --ob-r px. Text
   colors like a stock button (user, v519: coupling it to the outline was rejected -- and a
   Font.Color set back to its DEFAULT never reaches the dfm, so there was no way out of it). */
.vcl-outline-btn {
  cursor: pointer;
  background: transparent;
  border: var(--ob-w, 2px) solid var(--ob-c, #3390ff);
  border-radius: var(--ob-r, 8px);
  color: var(--tp-btn-fg, #1a1a1a);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
/* hover/active: a neutral translucent tint -- derived-from-var mixing needs color-mix(),
   which the Cat S41 era WebView does not have */
.vcl-outline-btn:hover:not(:disabled) { background: rgba(127, 127, 127, 0.08); }
.vcl-outline-btn:active:not(:disabled) { background: rgba(127, 127, 127, 0.16); }
.vcl-outline-btn:disabled { cursor: not-allowed; opacity: 0.55; }

/* TOutlinePanel */
/* TOutlinePanel -- a flat CARD: outline + rounded corners + drop shadow, a container like
   TPanel (the {{Children}} slot makes it droppable-into). The Outline/Shadow props land as
   inline CSS vars (manifest cssVars); Shadow=False rides the data-op-shadow attribute the
   template stamps, because a boolean cannot become a CSS var worth reading. */
.vcl-outline-panel {
  box-sizing: border-box;
  background: var(--tp-card-bg, #ffffff);
  border: var(--op-w, 1px) solid var(--op-c, #c0c0c0);
  border-radius: var(--op-r, 10px);
  box-shadow: 0 2px var(--op-shsz, 12px) var(--op-shc, #c0c0c0);
}
.vcl-outline-panel[data-op-shadow="False"] { box-shadow: none; }
.vcl-outline-panel-cap {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: inherit;
}

/* TpAdminButton */
/* TpAdminButton (v372) — the DimWhenLocked appearance: visible but disabled + greyed, so users can
   SEE what a higher level would unlock (the reference's dim+grey mode). Hidden mode reuses
   .tp-access-hidden from TpAuthSetup's stylesheet. */
.tp-admin-dim {
  opacity: 0.45;
  filter: grayscale(60%);
  pointer-events: none;
}

/* TpAuthSetup */
/* TpAuthSetup (v300) — access-gating visibility class. Layered OVER a control's own Visible
   (the gate never touches the worker-side Visible mirror — same stance as the reference). */
.tp-access-hidden { display: none !important; }

/* TPayPal */
/* TPayPal is a NON-VISUAL component: it uses the shared .vcl-nonvisual puck (display:none at runtime,
   revealed at design by ServeDesign's `.vcl-nonvisual{display:block!important}`; the instance name is
   the ::after label). Both rules live in components/Data/TMySQLDatabase/styles.css (loaded via
   /components.css). Here we override only the puck GLYPH with a PayPal-blue badge so it's recognizable
   on the design canvas. The element still exists (hidden) at runtime so method calls can target it. */

.vcl-nonvisual.vcl-paypal {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect x='.5' y='.5' width='23' height='23' rx='4' fill='%23003087'/%3E%3Cpath d='M9 6h5.2c2 0 3.3 1.2 3 3.1-.3 2.2-2 3.3-4.2 3.3H11l-.6 4H8.2L9 6z' fill='%23ffffff'/%3E%3Cpath d='M12.4 9h3.6c2 0 3.3 1.1 3 3-.3 2.2-2 3.3-4.2 3.3h-1.9l-.6 3.7h-2.1l1.2-7.7' fill='%23009cde' opacity='.9'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
}

/* TpBadge */
.vcl-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 11px;
  background: #e8551f;
  color: #fff;
  font: bold 10px Tahoma, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  box-sizing: border-box;
}

/* TpBars */
.vcl-bars {
  background: #fff;
  border: 1px solid #c9c9d4;
  border-radius: 4px;
  box-sizing: border-box;
}

/* TpClock */
.vcl-clock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #1a1a2e;
  color: #00e5a0;
  font: bold 14px Consolas, monospace;
  border-radius: 4px;
  letter-spacing: 1px;
  box-sizing: border-box;
}

/* TPDFView */
/* TPDFView — the browser's native PDF viewer in an <iframe>. The src is server-rendered from the URL
   prop, so a configured viewer shows the real PDF even on /design (no JS needed); `credentialless` lets
   a cross-origin/CORS PDF load while the page stays cross-origin-isolated (same as TYouTube). Runtime
   URL change is the SetURL method. #525659 is the native viewer's surface (shows while blank/loading). */

.vcl-pdfview {
  box-sizing: border-box;
  display: block;
  border: 1px solid #c2c7d0;
  background: #525659;
}

/* TpGraph */
/* TpGraph — the host carries the geometry (inline {Style}); client.js sizes + draws the <canvas>.
   On /design (no client.js) the canvas stays empty, so a faint chart-sketch background on the host
   gives a design-time hint. At runtime the canvas paints an opaque chart over it (the first draw op
   fills the background), and — with no app data — the engine synthesises a sample curve + state band. */

.vcl-graph {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  background: #ffffff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='90' preserveAspectRatio='none'%3E%3Crect width='160' height='90' fill='%23fafafa'/%3E%3Cg stroke='%23ececec'%3E%3Cline x1='28' y1='8' x2='28' y2='74'/%3E%3Cline x1='28' y1='74' x2='152' y2='74'/%3E%3Cline x1='28' y1='30' x2='152' y2='30'/%3E%3Cline x1='28' y1='52' x2='152' y2='52'/%3E%3C/g%3E%3Cpolyline fill='none' stroke='%23d62728' stroke-width='1.5' points='28,58 50,40 72,48 94,24 116,34 138,18 152,26'/%3E%3Crect x='28' y='78' width='40' height='8' fill='%239e9e9e'/%3E%3Crect x='68' y='78' width='34' height='8' fill='%231f77b4'/%3E%3Crect x='102' y='78' width='50' height='8' fill='%23ff7f0e'/%3E%3C/svg%3E") center / cover no-repeat;
}
/* v516: touch-action none so a finger drag reaches the pointer handlers (the
   scrollbar) instead of becoming a page pan; webhead pages cannot scroll anyway. */
.vcl-graph-canvas { display: block; width: 100%; height: 100%; touch-action: none; }

/* TpSparkline */
.vcl-spark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #f7f7fa;
  border: 1px solid #c9c9d4;
  border-radius: 4px;
  box-sizing: border-box;
  overflow: hidden;
}

/* TpToggle */
.vcl-toggle {
  cursor: pointer;
  border: 1px solid #2a7d6f;
  border-radius: 14px;
  background: linear-gradient(to bottom, #3ec8b0, #2a9d8a);
  background-clip: padding-box;
  color: #fff;
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-toggle:active { background: linear-gradient(to bottom, #2a9d8a, #3ec8b0); }

/* .tp-on — set imperatively by the shim's Activate method via the DOM bridge (v038) */
.vcl-toggle.tp-on {
  background: linear-gradient(to bottom, #ffd34d, #f0a818);
  border-color: #c47e00;
  color: #3a2a00;
  box-shadow: 0 0 6px 1px rgba(240, 168, 24, 0.7);
}

/* TQRCode */
/* TQRCode — hand-rolled QR code component. The host carries the geometry (inline {Style});
   client.js renders a crisp SVG that fills the box (width/height 100% + viewBox), so the QR is
   sharp at any size. On /design (no client.js) the static template SVG skeleton is shown. */

.vcl-qrcode {
  box-sizing: border-box;
  display: block;
  overflow: hidden;
  background: transparent;
  line-height: 0;          /* avoid inline-SVG baseline gap */
}
.vcl-qrcode > svg { display: block; width: 100%; height: 100%; }

/* TScrollStory */
/* TScrollStory — the scroll-stopping story section (v313). The comp's root element IS the scroll
   runway (its designed height); .tp-story-sticky pins to the window scrollport while the runway
   passes. Content colours come from the slides themselves — no theme tokens by design. */
.vcl-scrollstory { overflow: visible; }

/* the static design/template placeholder (replaced by client.js) */
.tp-story-design {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px;
  background: linear-gradient(160deg, #1b2735 0%, #090a0f 100%);
  color: #fff; text-align: center; overflow: hidden;
}
.tp-story-design-head { font-size: 22px; font-weight: bold; letter-spacing: .5px; }
.tp-story-design-sub  { font-size: 11px; opacity: .65; }
/* v476: the design-time placeholder TEXT moved out of template.html and into CSS-generated content,
   scoped to the design canvas (.tp-design is put on the form host by dfmRendererUnit when DesignMode).
   Before this it was baked into every EXPORTED page: a scroll-story landing page shipped the literals
   "Scroll Story" and "right-click -> Slides... to add slides" to Google, in every language.
   CSS ::before content is not indexed, and the designer still sees exactly what it saw. */
.tp-design .tp-story-design-head::before { content: 'Scroll Story'; }
.tp-design .tp-story-design-sub::before  { content: 'right-click \2192  Slides\2026  to add slides'; }
.tp-story-design-dots span, .tp-story-dots span {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: rgba(255,255,255,.45); margin: 0 3px;
}
.tp-story-design-badge {
  position: absolute; top: 6px; right: 8px; font-size: 10px; color: #fff;
  background: rgba(0,0,0,.55); border-radius: 9px; padding: 2px 8px;
}

/* runtime */
.tp-story-sticky {
  position: sticky; top: 0; width: 100%; overflow: hidden; background: inherit;
}
.tp-story-slide { position: absolute; inset: 0; }
.tp-story-bg {
  position: absolute; inset: -1px; background-size: cover; background-repeat: no-repeat;
  will-change: transform, opacity;
}
.tp-story-shade { position: absolute; inset: 0; pointer-events: none; }
.tp-story-content {
  position: absolute; inset: 0; display: flex; flex-direction: column; gap: 12px;
  padding: 6%; box-sizing: border-box;
}
.tp-pos-center       { align-items: center;     justify-content: center; text-align: center; }
.tp-pos-top          { align-items: center;     justify-content: flex-start; text-align: center; }
.tp-pos-bottom       { align-items: center;     justify-content: flex-end; text-align: center; }
.tp-pos-left         { align-items: flex-start; justify-content: center; text-align: left; }
.tp-pos-right        { align-items: flex-end;   justify-content: center; text-align: right; }
.tp-pos-top-left     { align-items: flex-start; justify-content: flex-start; text-align: left; }
.tp-pos-top-right    { align-items: flex-end;   justify-content: flex-start; text-align: right; }
.tp-pos-bottom-left  { align-items: flex-start; justify-content: flex-end; text-align: left; }
.tp-pos-bottom-right { align-items: flex-end;   justify-content: flex-end; text-align: right; }
.tp-story-heading { font-size: clamp(26px, 5vw, 52px); font-weight: bold; line-height: 1.1; margin: 0; text-shadow: 0 2px 12px rgba(0,0,0,.35); }
.tp-story-sub     { font-size: clamp(15px, 2.2vw, 24px); opacity: .92; text-shadow: 0 1px 8px rgba(0,0,0,.35); }
.tp-story-body    { font-size: clamp(13px, 1.5vw, 17px); max-width: 46em; opacity: .88; white-space: pre-line; }
.tp-story-cta {
  font: inherit; font-size: clamp(13px, 1.6vw, 17px); font-weight: bold; cursor: pointer;
  color: inherit; background: rgba(255,255,255,.14); border: 1px solid currentColor;
  border-radius: 24px; padding: 10px 26px; margin-top: 6px; backdrop-filter: blur(2px);
}
.tp-story-cta:hover { background: rgba(255,255,255,.28); }
.tp-story-dots {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 4px; z-index: 5;
}
.tp-story-dots span { cursor: pointer; transition: background .2s, transform .2s; margin: 0; }
.tp-story-dots span.tp-on { background: #fff; transform: scale(1.35); }
.tp-story-hint {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%); z-index: 5;
  width: 22px; height: 22px; border-right: 3px solid rgba(255,255,255,.85);
  border-bottom: 3px solid rgba(255,255,255,.85); rotate: 45deg;
  animation: tpStoryHint 1.6s ease-in-out infinite; pointer-events: none;
}
@keyframes tpStoryHint {
  0%, 100% { transform: translate(-50%, 0); opacity: .85; }
  50%      { transform: translate(-50%, 8px); opacity: .35; }
}

/* TSynEdit */
/* TSynEdit (v618, tp-webui P3b) -- the IDE's editing surface, folded into an
   ordinary catalog component. Themeable the way every comp added since v608 is:
   each colour is a variable with ITS OWN OLD VALUE as the fallback, so a user's
   program that ships this alone renders as it always did, while a host that
   defines the variables (the IDE serves them at /ide-theme.css) gets an editor
   in its own theme. */
.tp-code {
  box-sizing: border-box;
  position: relative;
  display: flex;
  overflow: hidden;
  background: var(--tp-bg, #fff);
  color: var(--tp-text, #000);
  font: 13px/1.35 Consolas, "Courier New", monospace;
}
.tp-code-gut {
  flex: 0 0 auto;
  width: 52px;
  text-align: right;
  padding: 2px 6px 0 0;
  box-sizing: border-box;
  user-select: none;
  opacity: .75;
  border-right: 1px solid var(--tp-line, #d8d8d8);
}
/* *** v757 (the user: "fix text selection in code view, it is acting strange
   and selects far more than one wants"). THE BROWSER WAS SELECTING TOO. ***
   This surface draws its OWN selection -- a span inside the line, see the note
   further down -- and every line here is real DOM text, so a drag also started
   the browser's native selection on top of it. Two selections at once, one of
   them sweeping across the gutter, the find bar and whatever else the pointer
   passed, and neither agreeing with the other about what is selected.
     The gutter has said user-select:none since it was written; the text did
   not, which is the whole of it. Nothing here needs preventDefault (which
   would cost the focus this element takes through its tabindex). */
.tp-code-area {
  flex: 1 1 auto;
  padding: 2px 0 0 6px;
  box-sizing: border-box;
  white-space: pre;
  overflow: hidden;
  position: relative;
  user-select: none;
}
/* v757 (the user: "mouse not having text caret, only arrow all the time"). An
   I-BEAM, because this is text you can put a caret in and drag a selection
   across -- default was saying the opposite of what the surface does. The area
   carries it too, so the empty space to the right of a short line still reads
   as text. The gutter keeps its pointer: clicking there sets a breakpoint. */
.tp-code-area, .tp-code .ln { cursor: text; }
.tp-code .ln { height: 1.35em; }
.tp-code .ln.brk { border-radius: 2px; }
.tp-code .gl { height: 1.35em; cursor: pointer; }
.tp-code .gl.brk { color: #fff; background: #4040c0; border-radius: 2px; }
.tp-code .gl.cur { font-weight: 700; opacity: 1; }
/* the five Pascal token classes, which are the five the IDE's own highlighter
   paints -- SynHighlighterPas's classes read, never its code */
.tp-code .k { color: var(--tp-kw, #0000c0); font-weight: 600; }
.tp-code .s { color: var(--tp-str, #a31515); }
.tp-code .c { color: var(--tp-cmt, #008000); font-style: italic; }
.tp-code .n { color: var(--tp-num, #098658); }
/* the selection band is a SPAN inside the line, not a CSS ::selection, because
   the line is already highlighted markup and only its middle piece is selected */
.tp-code .selspan { background: var(--tp-sel, #cbe4fb); }
.tp-code-caret {
  position: absolute; width: 2px; background: var(--tp-text, #000);
  animation: tp-code-blink 1s step-end infinite;
}
@keyframes tp-code-blink { 50% { opacity: 0; } }
/* *** v758 THE MINIMAP (the user: "code editor: map view is missing, from right
   side of editor"). *** A flex sibling of the gutter and the text, so it takes
   its width out of the row and needs no absolute positioning and no z-order.
   --tp-map-w is written by the component when the strip is sized: the status
   bar and the find bar are pinned to the RIGHT EDGE, which is the strip's now,
   and one variable moves both rather than two rules that could disagree. */
.tp-code-map {
  flex: 0 0 auto; width: 0; display: none;
  position: relative; overflow: hidden;
  cursor: pointer; user-select: none;
  border-left: 1px solid var(--tp-line, #d8d8d8);
  background: var(--tp-bg, #fff);
}
.tp-code.hasmap .tp-code-map { display: block; }
.tp-code-map canvas { position: absolute; left: 0; top: 0; display: block; }
/* THE VIEWPORT BAND IS A GLAZE, not a fixed tint -- v757's hover lesson, where
   a #f2f7fd highlight on an #f2f8fd surface fired correctly and was invisible.
   --tp-text is the one colour guaranteed to contrast with the ground it sits
   on, in either theme and at whatever base colour the user picks. */
.tp-code-mapv {
  position: absolute; left: 0; right: 0; top: 0; height: 0;
  background: var(--tp-text, #000); opacity: .13; pointer-events: none;
}
.tp-code-bar {
  position: absolute; right: var(--tp-map-w, 0px); bottom: 0; padding: 1px 6px;
  font: 11px "Segoe UI", Tahoma, sans-serif; opacity: .7;
  background: var(--tp-bg, #fff);
}
/* the strip's toggle -- the same glyph (U+25A4) the VCL editor bar's button
   wears, so the gesture did not change name when the editor did. PINNED to the
   corner and NOT inside the status bar: the bar is positioned to the LEFT of
   the strip, so a toggle living in it moved 90px the instant it was pressed. */
.tp-code-mapbtn {
  position: absolute; right: 2px; bottom: 0; z-index: 6;
  padding: 0 3px; cursor: pointer; opacity: .35; user-select: none;
  background: var(--tp-bg, #fff);
  font: 13px "Segoe UI Symbol", "Segoe UI", Tahoma, sans-serif;
}
.tp-code-mapbtn:hover { background: var(--tp-hover, rgba(0,0,0,.07)); }
.tp-code-mapbtn.on { opacity: 1; }
.tp-code-bar { right: calc(var(--tp-map-w, 0px) + 18px); }

/* THE HOVER PREVIEW (the VCL's MinimapHover, v337): hover the strip and a
   full-size nine-line window of that part of the file opens beside it. It is
   the answer to the one thing an overview cannot do -- the shape tells you
   where a block is and never what it says. Same monospace as the editor,
   because a preview in another font is a preview of another document. */
.tp-code-peek {
  position: absolute; z-index: 9; display: none;
  max-width: 70%; overflow: hidden; white-space: pre;
  padding: 3px 6px;
  background: var(--tp-bg, #fff); color: var(--tp-text, #000);
  /* THE VCL PANEL'S BLUE 2px FRAME IS NOT COPIED, and that is a decision.
     --tp-accent does not exist in this IDE's theme (it would have been the
     fallback for ever) and --tp-link, which does, is GREEN in dark mode --
     MEASURED #6ec86e, and a loud green box over the code is not what that
     frame was for. The completion popup right below already says how a
     floating panel separates itself here: a hairline and a real shadow. One
     idiom, and it is right at every base colour. */
  border: 1px solid var(--tp-line, #d8d8d8);
  box-shadow: 0 3px 10px rgba(0,0,0,.28);
  pointer-events: none;   /* it follows the pointer; it must never catch it */
}
.tp-code-peek.on { display: block; }
.tp-code-peek .pk { height: 1.35em; }
.tp-code-peek .pk.cur { background: var(--tp-sel, #cbe4fb); }
.tp-code-peek .pn { display: inline-block; width: 4em; text-align: right;
                    margin-right: 8px; opacity: .55; }

/* THE CONTEXT MENU -- mainUnit's seEditor popup (Cut/Copy/Paste, Code
   Completion, Show Minimap, Minimap Hover, Fit Minimap). Inside the element,
   like the completion popup and for the same reason: it moves and clips with
   the editor and needs nobody's z-order but this one's. */
.tp-code-menu {
  position: absolute; z-index: 10; display: none; min-width: 168px;
  padding: 3px 0; user-select: none;
  background: var(--tp-bg, #fff); color: var(--tp-text, #000);
  border: 1px solid var(--tp-line, #d8d8d8);
  box-shadow: 0 2px 8px rgba(0,0,0,.28);
  font: 12px "Segoe UI", Tahoma, sans-serif;
}
.tp-code-menu.on { display: block; }
.tp-code-menu .mi { padding: 3px 10px 3px 24px; white-space: nowrap; cursor: default; position: relative; }
.tp-code-menu .mi:hover { background: var(--tp-hover, rgba(0,0,0,.07)); }
.tp-code-menu .mi.off { opacity: .4; }
.tp-code-menu .mi.off:hover { background: none; }
/* the check mark, drawn where a VCL menu draws it -- in the gutter left of the
   caption, so the captions stay aligned whether an item is checked or not */
.tp-code-menu .mi.chk::before { content: "\2713"; position: absolute; left: 8px; }
.tp-code-menu .msep { height: 0; margin: 3px 0; border-top: 1px solid var(--tp-line, #d8d8d8); }
/* the find bar: the surface's own chrome, authored once */
.tp-code-find {
  position: absolute; right: calc(var(--tp-map-w, 0px) + 8px); top: 2px; z-index: 8; display: none;
  align-items: center; gap: 4px; padding: 3px 6px;
  background: var(--tp-bar, #f0f0f0); border: 1px solid var(--tp-line, #d8d8d8);
  border-radius: 3px; font: 12px "Segoe UI", Tahoma, sans-serif;
}
.tp-code-find.on { display: flex; }
.tp-code-find input[type=text] { font: inherit; width: 120px; }
.tp-code-find label { opacity: .8; user-select: none; }
.tp-code-find .fbtn { cursor: default; padding: 0 4px; user-select: none; border: 1px solid transparent; }
.tp-code-find .fbtn:hover { background: var(--tp-sel, #cbe4fb); border-color: var(--tp-line, #d8d8d8); }
.tp-code-find .fmsg { opacity: .7; min-width: 60px; }
/* the completion popup (v620): the surface's own list, wherever its items come
   from. It is INSIDE the element rather than a floating window, so it moves and
   clips with the editor and needs nobody's z-order but this one's. */
.tp-code-comp {
  position: absolute; z-index: 9; display: none;
  min-width: 200px; max-width: 320px; max-height: 176px; overflow: auto;
  background: var(--tp-bg, #fff); color: var(--tp-text, #000);
  border: 1px solid var(--tp-line, #d8d8d8);
  box-shadow: 0 2px 6px rgba(0,0,0,.25);
  font: 12px "Segoe UI", Tahoma, sans-serif;
}
.tp-code-comp.on { display: block; }
.tp-code-comp .ci { padding: 1px 8px; white-space: nowrap; cursor: default; }
.tp-code-comp .ci.on { background: var(--tp-sel, #cbe4fb); }

/* TDBCalendar */
/* TDBCalendar — day/month appointment calendar widget styles.
   Ported from the reference IDE (tpwebide/styles/classic.css, the .vcl-cal block).
   Adaptations for this package:
     - .cal-app-car renamed to .cal-app-object (the appointment first line).
     - pat4.gif / pat7.png hatches replaced with a CSS repeating-linear-gradient
       so NO binary assets are needed.
     - wrapper + toolbar styles added at the top.
   All CSS-variable usages (var(--cal-line) etc.) are kept as-is. */

/* ---- wrapper + toolbar (host chrome around the .vcl-cal grid) ---- */
.vcl-dbcalendar-wrap {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;
}
.vcl-dbcalendar-toolbar {
  flex: 0 0 auto;
  position: relative;          /* w011: the centre band is positioned on THIS */
  display: flex;
  align-items: center;
  flex-wrap: wrap;   /* N11: a too-narrow calendar wraps the toolbar instead of clipping its buttons */
  gap: 6px;
  padding: 3px 6px;
  background: linear-gradient(#f7f7f7, #ececec);
  border-bottom: 1px solid #cfcfcf;
  font-size: 12px;
}
.vcl-dbcalendar-toolbar button { font: inherit; padding: 1px 8px; cursor: pointer; flex: 0 0 auto; }   /* N11: keep buttons at size (don't shrink them away) */
/* N11: a long localized title (e.g. "onsdag 30. september 2026") ellipsizes rather than pushing the Day/Month buttons off. */
.vcl-dbcalendar-toolbar .tp-cal-title { text-transform: uppercase; font-weight: bold; margin: 0 4px;
  flex: 0 0 20em; width: 20em; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vcl-dbcalendar-toolbar .tp-cal-spacer { flex: 1 1 auto; }
/* w008: the toolbar's three bands. The view buttons keep their size on the
   left; the centre group takes the room that is left and centres the date in
   it, with its two step buttons either side. The date is a CONTROL now (it
   goes to today), so it gets a hover the way the buttons do. */
/* sqlconvert w011 centred this band on the BAR by taking it out of flow
   (absolute at 50%): in a plain row it was centred in whatever room the
   buttons left, half their width off the real middle.
     sqlconvert w018: BACK IN FLOW, centred the other way. An absolute band
   takes no room from its siblings, so when the window narrowed it drew straight
   over the buttons -- nothing pushed, nothing wrapped. Three columns instead:
   the left and right groups are flex:1 1 0 (an equal basis of ZERO, so the
   spare width splits evenly either side of the centre, whatever each side
   holds) and the band is 0 0 auto between them. Because it takes room, a bar
   too narrow for all three wraps the band onto a second row (flex-wrap above)
   rather than overlapping. Stacked, never over. */
.vcl-dbcalendar-toolbar .tp-cal-viewbtns {
  display: flex; align-items: center; gap: 6px;
  flex: 1 1 0; justify-content: flex-start;
}
.vcl-dbcalendar-toolbar .tp-cal-right {
  display: flex; align-items: center; gap: 6px;
  flex: 1 1 0; justify-content: flex-end;
}
.vcl-dbcalendar-toolbar .tp-cal-centre {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  flex: 0 0 auto;
}
.vcl-dbcalendar-toolbar .tp-cal-title:hover { text-decoration: underline; }
/* w017: the host's menu button (menuItems) and the list it drops. The button
   carries a DRAWN hamburger so it is usable with no host stylesheet at all --
   on a ::before, because a host that repaints a toolbar button's face per state
   would wipe a background-image set on the button itself. */
.vcl-dbcalendar-toolbar .tp-cal-menu { position: relative; min-width: 2.6em; }
.vcl-dbcalendar-toolbar .tp-cal-menu::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'><g fill='%23555'><rect x='2' y='3.5' width='14' height='2' rx='1'/><rect x='2' y='8' width='14' height='2' rx='1'/><rect x='2' y='12.5' width='14' height='2' rx='1'/></g></svg>") center / 18px 18px no-repeat;
}
.vcl-dbcalendar-toolbar .tp-cal-menupop {
  position: absolute; z-index: 40; min-width: 200px;
  display: flex; flex-direction: column;
  background: #ffffff; border: 1px solid #b5b5b5; border-radius: 3px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); padding: 3px 0;
  font-size: 12px; text-align: left;
}
.vcl-dbcalendar-toolbar .tp-cal-menuitem {
  padding: 6px 18px; cursor: pointer; white-space: nowrap; color: #14212b;
}
.vcl-dbcalendar-toolbar .tp-cal-menuitem:hover,
.vcl-dbcalendar-toolbar .tp-cal-menuitem:focus { background: #dceaf9; outline: none; }
.vcl-dbcalendar-toolbar .tp-cal-menusep { height: 1px; margin: 4px 10px; background: #dcdcdc; }
.vcl-dbcalendar-wrap .vcl-cal { flex: 1 1 auto; min-height: 0; }
/* C25: pre-boot placeholder shown until the client.js renderer runs. BOTH the running app AND the /design canvas
   load this client.js and REPLACE it (innerHTML='') with a real grid — /design draws a STATIC grid from the props
   (no /api/db, per the C17 design gate), the running app draws the live data-bound calendar. (Earlier this comment
   claimed /design didn't load the client.js at all — that stopped being true when the design canvas gained the
   inert static render.) */
.tp-cal-ph {
  width: 100%; height: 100%; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  text-align: center; color: #7a8aa0;
  background:
    repeating-linear-gradient(0deg,  #eef2f8 0 1px, transparent 1px 26px),
    repeating-linear-gradient(90deg, #eef2f8 0 1px, transparent 1px 88px);
}
.tp-cal-ph-icon { font-size: 40px; line-height: 1; opacity: .75; }
.tp-cal-ph-title { font-weight: bold; font-size: 15px; color: #4a7; }
.tp-cal-ph-sub { font-size: 11px; color: #9aa6ba; max-width: 80%; }

/* TCalendar — day/month appointment calendar; one box, View toggles which view.
   The whole widget is built by bootstrap's renderCalendar(el) into child layers;
   every rule here is scoped under .vcl-cal so it never leaks to the host page. */
.vcl-cal {
  position: relative;
  overflow: hidden;
  background: #fff;
  box-sizing: border-box;
  font-family: Tahoma, Arial, sans-serif;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}
.vcl-cal .cal-header-layer,
.vcl-cal .cal-grid-layer,
/* touch-action:none (N1): the day view's drag surface must not let a touch pan/scroll the page — the finger
   drives move/resize/create instead. Set on the layer AND the blocks/handle so it holds wherever a drag starts. */
.vcl-cal .cal-apps-layer { position: absolute; inset: 0; touch-action: none; }
/* time-gutter sidebars (CalColors.Sidebar; bg set inline by renderCalendar) */
.vcl-cal .cal-sidebar { position: absolute; pointer-events: none; z-index: 0; }
/* grid line colour = CalColors.Lines (one colour). Solid on the hour, DASHED at the half-hour —
   faithful to the original (the dash is the distinction; .cal-grid-line-hour overrides via later order). */
.vcl-cal .cal-grid-line {
  position: absolute; left: 0; right: 0;
  border-bottom: 1px dashed var(--cal-line, #99BCE8); pointer-events: none;
}
.vcl-cal .cal-grid-line-hour { border-bottom: 1px solid var(--cal-line, #99BCE8); }
.vcl-cal .cal-grid-column {
  position: absolute; top: 0; bottom: 0;
  border-left: 1px solid var(--cal-line, #99BCE8); pointer-events: none;
}
/* Extra/waiting columns: a FAINT translucent type tint over the root CalColors.Background (which shows
   through), so the background applies to ALL positions while the column-type cue is kept. */
.vcl-cal .cal-col-extra   { position: absolute; background: rgba(255, 110, 210, 0.10); z-index: 0; pointer-events: none; }
.vcl-cal .cal-col-waiting { position: absolute; background: rgba(120, 120, 150, 0.10); z-index: 0; pointer-events: none; }
/* TimeLine.ShowTimeButtons corner buttons (AddStart/AddEnd/DelStart/DelEnd). C12: the 8 /img/cal/*.png assets
   were MISSING (broken images). Replaced with self-contained inline-SVG data-URIs — COEP-safe, no binary assets
   (rule K6). A top edge-bar = "start", a bottom edge-bar = "end"; a green "+" = add, a red "−" = delete. Default
   is all-grey; hover colours the +/− sign (green/red), matching the original grayscale-std → colour-hover cue. */
.vcl-cal .cal-timebtn { position: absolute; width: 18px; height: 24px; z-index: 6; cursor: pointer;
  background-size: contain; background-repeat: no-repeat; background-position: center; }
.vcl-cal .cal-tb-addstart       { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='3' width='12' height='2' rx='1' fill='%23888'/><path d='M9 7v8M5 11h8' stroke='%23888' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-addstart:hover { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='3' width='12' height='2' rx='1' fill='%23888'/><path d='M9 7v8M5 11h8' stroke='%232e7d32' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-addend         { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='19' width='12' height='2' rx='1' fill='%23888'/><path d='M9 9v8M5 13h8' stroke='%23888' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-addend:hover   { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='19' width='12' height='2' rx='1' fill='%23888'/><path d='M9 9v8M5 13h8' stroke='%232e7d32' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-delstart       { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='3' width='12' height='2' rx='1' fill='%23888'/><path d='M5 11h8' stroke='%23888' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-delstart:hover { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='3' width='12' height='2' rx='1' fill='%23888'/><path d='M5 11h8' stroke='%23c62828' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-delend         { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='19' width='12' height='2' rx='1' fill='%23888'/><path d='M5 13h8' stroke='%23888' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-tb-delend:hover   { background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 24'><rect x='3' y='19' width='12' height='2' rx='1' fill='%23888'/><path d='M5 13h8' stroke='%23c62828' stroke-width='2' stroke-linecap='round'/></svg>"); }
.vcl-cal .cal-header-bg {
  position: absolute; z-index: 9;
  background: var(--cal-hdr-bg, linear-gradient(to bottom, #BFD4EF, #D3E2F4));
  border-bottom: 1px solid #ABC7EC;
}
.vcl-cal .cal-weekno {
  position: absolute; z-index: 11;
  display: flex; align-items: center; justify-content: center;
  color: #cc0000; font-weight: bold; font-size: 12px;
  overflow: hidden; pointer-events: none;
}
.vcl-cal .cal-column-header {
  position: absolute; top: 0;
  border-bottom: 1px solid #ABC7EC;
  border-left: 1px solid var(--cal-line, #99BCE8);
  background: var(--cal-hdr-bg, linear-gradient(to bottom, #BFD4EF, #D3E2F4));
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  font-size: 12px; color: #3C408C; z-index: 10; font-weight: bold;
  box-sizing: border-box; overflow: hidden; padding: 0 4px;
}
.vcl-cal .cal-column-header span { font-weight: normal; font-size: 10px; opacity: 0.8; }
.vcl-cal .cal-time-label {
  position: absolute; left: 2px;
  font-size: 12px; color: #334455; pointer-events: none; font-weight: bold;
}
.vcl-cal .cal-time-label b {
  font-size: 9px; vertical-align: top; text-decoration: underline; margin-left: 1px;
}
/* *** w030: THE SAVE NOTICE. *** drawNotice() in the original's day script.
   ONE OPACITY OVER THE WHOLE THING, which is what ctx.globalAlpha = 0.55 does:
   it is set before the fill AND before the text and the image, and restore()
   comes after both -- so the green, the words and the tick are all 55% there
   and the break band underneath shows through all of them. An opacity on the
   container is the same statement.
     THE GREEN IS THE REFERENCE'S OWN: var bgoCol = "#B8FFB0", the colour it
   also fills a paid appointment with.
     Both the colour and the icon are CSS VARIABLES because they are the two
   things an app will want to say for itself -- this component should not carry
   one shop's tick. The default is a plain green check of the same 50x39. */
.vcl-cal .cal-notice {
  position: absolute; z-index: 12;
  display: flex; align-items: center; justify-content: center; gap: 3px;
  opacity: 0.55;
  background: var(--cal-notice-bg, #B8FFB0);
  color: #000;
  font-family: Tahoma, Arial, sans-serif;
  font-size: 26px;
  pointer-events: none;    /* it is a message, not a surface -- a drag under it still works */
  overflow: hidden; box-sizing: border-box;
}
/* "if (vSize < 40) ctx.font = '20px Tahoma'" */
.vcl-cal .cal-notice.cal-notice-small { font-size: 20px; }
.vcl-cal .cal-notice-icon {
  flex: 0 0 auto; width: 50px; height: 39px;
  background: center / contain no-repeat
    var(--cal-notice-icon, url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='50' height='39' viewBox='0 0 50 39'><path d='M5 21 L18 33 L45 5' fill='none' stroke='%234FA24A' stroke-width='9' stroke-linecap='round' stroke-linejoin='round'/></svg>"));
}
.vcl-cal .cal-notice.cal-notice-small .cal-notice-icon { width: 32px; height: 25px; }
.vcl-cal .cal-break-area {
  position: absolute;
  /* *** ONE COLOUR, WHICH IS WHAT THE REFERENCE FILLS IT WITH. *** (w017.)
     drawBreak1() in the original's day script sets one fillStyle, rects the
     band and fills it -- and never strokes it, though it sets a strokeStyle.
       It was a 45-degree two-grey hatch here. That came in (w010) to give the
     band contrast when it was two near-whites knocked back to 0.7 opacity, and
     it fixed a CONTRAST problem by adding a PATTERN. A flat grey fixes it
     without one, and the month view beside it (.cal-month-break) has been a
     solid bar all along -- its own comment says "matches the original (not the
     day view's hatch)". The two views now agree with each other and with what
     they were ported from.
       The pink hairlines went with the hatch: they were drawing the band's
     edges, and a band with a colour of its own does not need outlining.
       AND THE COLOUR IS THE GUTTER'S, stated as the variable and not as a copy
     of it (w017): the band is the same pale blue as the time strips down both
     edges, and stays that way if an app moves CalColors.Sidebar. That is nearer
     the reference than the flat grey this first became -- drawBreak1 fills the
     band with thCol, one of the calendar's own chrome colours. */
  background: var(--cal-sidebar, #EFF3FA);
  opacity: 1; z-index: 1; display: flex; align-items: center; padding-left: 12px;
  color: #ff3333; font-weight: bold; pointer-events: none;
}
/* Out-of-setup band: the expanded slice(s) of the day window OUTSIDE the open day-setup hours (the day view
   grows to show appointments before/after the work hours, or any app on a closed day). Cross-hatch via a CSS
   gradient (was pat7.png), behind the appointments (z-index 1). No opaque base — the root CalColors.Background
   shows through; the cross-hatch plus a faint dim mark the off-schedule slice. */
.vcl-cal .cal-outsetup {
  position: absolute; z-index: 1; pointer-events: none;
  background-color: rgba(0, 0, 0, 0.04);
  background-image: repeating-linear-gradient(45deg, rgba(0,0,0,.06) 0 6px, transparent 6px 12px);
  background-repeat: repeat;
}
.vcl-cal .cal-footer {
  position: absolute; left: 0; right: 0; bottom: 0;
  background-color: var(--cal-ftr-bg, #D3E2F4); border-top: 1px solid #99BCE8; z-index: 10;   /* N13: 0.5px → 1px */
  display: flex; align-items: center; padding: 0 12px;
  font-size: 11px; color: #334455; box-sizing: border-box;
}
.vcl-cal .cal-footer > div { margin-right: 22px; }
.vcl-cal .cal-version { margin-left: auto; margin-right: 4px; font-weight: bold; opacity: 0.5; }
/* Appointment blocks: one .cal-appointment per Appointments.Strings line,
   absolutely positioned in .cal-apps-layer. Type-specific background is set inline by
   drawApps; the default (type 0) colour lives here. */
/* Body/header backgrounds come from per-block CSS vars (drawCalApps sets them per state);
   :hover swaps to the state's Active shade. Header height/font/align are inline (AppHeader props). */
.vcl-cal .cal-appointment {
  position: absolute; box-sizing: border-box; overflow: hidden;
  border: 1px solid #8f8f8f; border-radius: 3px;
  background: var(--cal-app-bg, #DBEFFE); z-index: 5; cursor: pointer;
  touch-action: none;   /* N1: a touch on a block drags/resizes it, never scrolls the page */
}
.vcl-cal .cal-appointment:hover { background: var(--cal-app-bgh, #E6F3FE); }
/* N2: a visible keyboard-focus ring for appointments + day cells (mouse focus stays unadorned via :focus-visible). */
.vcl-cal .cal-appointment:focus-visible { outline: 2px solid #1a73e8; outline-offset: -1px; z-index: 11; }
/* N16: waiting-queue items (pos 0) had the .cal-app-waiting class but NO rule — they looked like normal blocks.
   Give the queue a distinct read: a dashed outline + a purple left accent + a faint accent wash. */
.vcl-cal .cal-appointment.cal-app-waiting {
  border-style: dashed;
  border-left: 3px solid #7e57c2;
  background-image: linear-gradient(90deg, rgba(126, 87, 194, 0.12), transparent 62%);
}
.vcl-cal .cal-app-header {
  font-size: 10px; text-align: center;
  border-bottom: 1px solid #8f8f8f; background: var(--cal-app-hdrbg, #C8E6FF);   /* N13: 0.5px → 1px */
  white-space: nowrap; overflow: hidden;
}
.vcl-cal .cal-app-content {
  padding: 4px;
  font-size: 11px; line-height: 1.2; overflow: hidden;
}
.vcl-cal .cal-app-object { font-weight: bold; margin-bottom: 2px; color: #000; }
.vcl-cal .cal-app-user { color: #444; }
.vcl-cal .cal-app-comment { color: #ff3333; font-style: italic; margin-top: 4px; font-size: 10px; }
/* --- Appointment icons (w017) -----------------------------------------------
   The reference day view draws 16x16 PNGs on every appointment: a type badge
   and the state flags across the header, a car in front of the object line and
   a person in front of the customer line. The pictures are the ORIGINAL FILES,
   generated into the block at the foot of this file by w017_appicons.py; these
   are the rules that place them. */
.vcl-cal .cal-ico {
  display: inline-block; width: 16px; height: 16px; vertical-align: -4px;
  background-size: 16px 16px; background-repeat: no-repeat; background-position: center;
}
/* A LINE ICON SITS 4px IN FRONT OF ITS TEXT -- the reference puts the icon at
   xStart+5 and the text at xStart+25, which is this. */
.vcl-cal .cal-app-content > div > .cal-ico { margin-right: 4px; }
/* A RECALL CAR (biler.Innkallingskunde): a 2px red square around the car icon.
   box-shadow rather than a border or an outline, because it must not move the
   text beside it by so much as a pixel. */
.vcl-cal .cal-ico-recall { box-shadow: 0 0 0 2px #ff0000; }
/* ...and the same fact when the icons are off: a 4px red bar over the line. */
.vcl-cal .cal-app-recallbar { height: 4px; background: #ff0000; margin: 0 0 2px; }
/* THE HEADER BADGES ARE POSITIONED, NOT FLOWED, so the time text stays centred
   in the band -- the canvas paints both over the same 18px header. The left
   group grows rightwards from the edge and the right group leftwards, which is
   what its fixed 20px slots did. pointer-events: none so a badge is not
   something you can fail to click the appointment through. */
.vcl-cal .cal-app-header { position: relative; }
.vcl-cal .cal-app-icons {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
  gap: 4px; pointer-events: none;
}
.vcl-cal .cal-app-icons.cal-ico-l { left: 1px; }
.vcl-cal .cal-app-icons.cal-ico-r { right: 1px; }
/* Blocking placeholder (type 3, the "udefinert" customer): a diagonal hatch (was pat4.gif), tiled,
   marks the position as blocked for that time. Body shows only the comment (no object/customer — drawCalApps
   skips them); the header keeps the time range. Pattern persists on :hover. */
.vcl-cal .cal-appointment.cal-app-blocking,
.vcl-cal .cal-appointment.cal-app-blocking:hover,
.vcl-cal .cal-month-app.cal-app-blocking {
  /* sqlconvert w010: ALTERNATING GREY, not grey-over-nothing. The old pair was
     rgba(0,0,0,.06) against transparent, which on a pale block is almost no
     pattern at all. Two greys at 8px read as stripes over any block colour --
     softened once after the first pass came out too strong. */
  background-image: repeating-linear-gradient(45deg,
      rgba(0,0,0,.045) 0 8px, rgba(0,0,0,.015) 8px 16px);
  background-repeat: repeat;
}
/* selected/dragged block + the bottom resize grip. */
.vcl-cal .cal-appointment.active {
  border-width: 1.5px; border-color: #000; box-shadow: 0 0 8px rgba(0,0,0,0.4); z-index: 10;
}
.vcl-cal .cal-resize-handle {
  position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
  cursor: ns-resize; background: linear-gradient(transparent, rgba(0,0,0,0.06));
  touch-action: none;   /* N1: touch-resize, don't scroll */
}
/* Drag-to-create guiding rectangle (the pending new appointment, above the blocks). */
.vcl-cal .cal-create-ghost {
  position: absolute; box-sizing: border-box;
  border: 1.5px dashed #3b78e7; background: rgba(59, 120, 231, 0.12);
  border-radius: 3px; z-index: 8; pointer-events: none;
  font-size: 10px; line-height: 1.3; color: #3b78e7; text-align: center; overflow: hidden;
}
/* While a calendar drag is in progress the cursor is forced everywhere. */
body.cal-drag-blocked, body.cal-drag-blocked * { cursor: not-allowed !important; }
body.cal-drag-move,    body.cal-drag-move *    { cursor: move !important; }
body.cal-drag-resize,  body.cal-drag-resize *  { cursor: ns-resize !important; }

/* N16: removed the dead .cal-month-placeholder rule — renderMonth always draws the real grid, the class is never
   emitted (grep-confirmed no client.js reference). */
/* Month view (M1): day-of-week header row, day boxes scaled to each day's own window. */
.vcl-cal .cal-month-dow {
  position: absolute; top: 0; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  background: var(--cal-hdr-bg, linear-gradient(to bottom, #BFD4EF, #D3E2F4));
  border-left: 1px solid var(--cal-line, #99BCE8); border-bottom: 1px solid #ABC7EC;
  /* 12px, which is .cal-column-header's size (w018; was 11). The two bands are
     the same header in two views and now differ in nothing. */
  font-size: 12px; font-weight: bold; color: #3C408C;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 2px;   /* N15: a long day name (localized) clips/ellipsizes, never overflows the header cell */
}
.vcl-cal .cal-month-gridbg { position: absolute; pointer-events: none; }    /* bg set inline = Month.GridColor */
.vcl-cal .cal-month-gutterbg { position: absolute; pointer-events: none; }  /* bg set inline = TimeLine.Color */
.vcl-cal .cal-month-hdrband {  /* full-width day-name band — covers the gutter corners (like the day view) */
  position: absolute; background: var(--cal-hdr-bg, linear-gradient(to bottom, #BFD4EF, #D3E2F4));
  border-bottom: 1px solid #ABC7EC; pointer-events: none;
}
.vcl-cal .cal-month-gutterline {  /* week-number gutter row separator */
  position: absolute; height: 0; border-top: 1px solid var(--cal-line, #99BCE8); pointer-events: none;
}
/* THE GRID = a continuous 1px lattice drawn BETWEEN the day rects (Month.GridColor via --cal-month-line). The
   rects are inset, so the lines sit in the margin with a few px each side, and run through the empty slots. */
.vcl-cal .cal-month-gridline.v { position: absolute; pointer-events: none; border-left: 1px solid var(--cal-month-line, var(--cal-line, #99BCE8)); }
.vcl-cal .cal-month-gridline.h { position: absolute; pointer-events: none; border-top:  1px solid var(--cal-month-line, var(--cal-line, #99BCE8)); }
.vcl-cal .cal-month-day {  /* a day RECT — its OWN outline (CalColors.Lines), inset from the grid lines; NOT the grid */
  position: absolute; box-sizing: border-box;
  border: 1px solid var(--cal-line, #99BCE8);
  overflow: hidden; cursor: pointer; background: #fff;
}
.vcl-cal .cal-month-day:hover { background: #f3f8ff; }
.vcl-cal .cal-month-day:focus-visible { outline: 2px solid #1a73e8; outline-offset: -2px; z-index: 6; }   /* N2: keyboard focus ring */
/* N16: removed the dead .cal-month-empty rule (empty lattice slots get no box element — grep-confirmed unused). */
.vcl-cal .cal-month-daynum {  /* top-RIGHT, grey — matches the original month view */
  position: absolute; right: 6px; top: 3px; font-size: 12px; font-weight: bold; color: #777; z-index: 3;
}
.vcl-cal .cal-month-weekno {  /* ISO week number in the left + right gutters (per the original) */
  /* 12px bold, which is .cal-time-label in the same gutter of the day view (w018; was 10px regular). */
  position: absolute; font-size: 12px; font-weight: bold; color: #334455; text-align: center; padding-top: 2px; pointer-events: none;
}
.vcl-cal .cal-month-day.cal-month-selected {  /* the calendar's focused day — yellow + red */
  /* N14: keep the border at 1px (was 2px) and add an inset red ring for the bold look — a 2px border shrank
     the content box by 1px vs unselected cells, so selecting jiggled the day's contents. Now zero shift. */
  background: var(--cal-selected, #FFFF77); border-color: #FF0000; box-shadow: inset 0 0 0 1px #FF0000; z-index: 5;
}
/* (w018 briefly added a second, RING mark here for "the day whose context menu
   is open". It is gone: a right-click on a day SELECTS that day, which is what
   the reference does — monthscript fills the box with bgSelCol and Main.pas
   moves selDate — so the rule above already draws it and there is only ever one
   focused day.) */
/* Month view (M2): in-box content — pos sub-column dividers, break bands, appointment rects.
   (No in-cell horizontal hour lines — the original month view keeps the day cells clean.) */
.vcl-cal .cal-month-poscol {
  position: absolute; width: 0; border-left: 1px solid var(--cal-line, #99BCE8);   /* N13: 0.5px → 1px */
  opacity: 0.4; pointer-events: none;
}
.vcl-cal .cal-month-break {   /* solid grey bar — matches the original (not the day view's hatch) */
  position: absolute; z-index: 1; pointer-events: none; background: #9e9e9e;
}
/* 'extra' column tint inside a month day cell (light pink, like the day view's cal-col-extra); backmost */
.vcl-cal .cal-month-coltint { position: absolute; background: #fff4fd; pointer-events: none; }
/* w020: the body colour arrives as a var (it was an inline backgroundColor),
   so `.vcl-cal .cal-month-app.cal-st-callin { background-color: ... }` in an
   app stylesheet wins on specificity alone. background-COLOR, never the
   shorthand: the blocking hatch is a background-image on the same element. */
.vcl-cal .cal-month-app {
  background-color: var(--cal-app-bg, #DBEFFE);
  position: absolute; z-index: 2; box-sizing: border-box;
  border: 1px solid rgba(0, 0, 0, 0.22);   /* N12: a faint outline so two same-colour bars in adjacent slots stay distinct (box-sizing keeps the bar's footprint) */
  pointer-events: none;
}
/* closed day (its day-setup window is blank). Month cell greyed; day view shows a Closed banner. */
.vcl-cal .cal-month-closed {
  background: repeating-linear-gradient(45deg, #f0f0f0, #f0f0f0 6px, #e7e7e7 6px, #e7e7e7 12px);
}
.vcl-cal .cal-closed {
  position: absolute; z-index: 5; display: flex; align-items: center; justify-content: center;
  background: repeating-linear-gradient(45deg, #f4f4f4, #f4f4f4 8px, #ececec 8px, #ececec 16px);
  color: #8a8a8a; font-size: 16px; font-style: italic; pointer-events: none;
}

/* N12: the gradient hatches (blocking apps, closed days, out-of-setup slice, break bands) VANISH under Windows
   High-Contrast (forced-colors strips background-image and overrides background-color) and usually in print
   (backgrounds off by default) — those regions would read as blank. Mark them with a system-colour outline so
   they stay distinguishable in both modes. (forced-color-adjust:auto is the default, so the outline is honoured.) */
@media (forced-colors: active), print {
  .vcl-cal .cal-appointment.cal-app-blocking,
  .vcl-cal .cal-month-app.cal-app-blocking,
  .vcl-cal .cal-month-closed,
  .vcl-cal .cal-closed,
  .vcl-cal .cal-outsetup,
  .vcl-cal .cal-break-area,
  .vcl-cal .cal-month-break {
    outline: 1px dashed GrayText;
    outline-offset: -1px;
  }
}

/* === BEGIN appointment icons -- GENERATED by w017_appicons.py === */
/* The reference calendar's own 16x16 files, ancillary chunks
   dropped and the pixels untouched. See w017_appicons.py. */
.vcl-cal .cal-ico-manual { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC4klEQVR42nxTTUgUYRh+vpnZ3XbV3WXVdDddKLUwM3/SCrFEiZLwEKiHBBFWzItBnoVS8GIhhXXw2N+lkEQ9dFprSeugnkKpcEmRDH92Z9efndnZ+embcQ2l8IUXPr533ud53mfej9ytSYceqgpkpRN47Cq6SwKwxEOjUly4lVBUaBpAsB8aCCEwMQQ2MztGDgJkUoBshwlSlEd3xUdN7rmEo8L54D04A1PTU4OqkGFJYzoFxooLc4O08hb+yQCtURWUgVUUfDpbjd6FMLT6HMj0jttrBhRF7S8pzuxMc2QjEpVRNeXH+PAA7EU1SIgCCs8UIMfrBR/RcO3UcewTcw47YxwkSe2RNTM04oACFoRbw7uh+3i+ED8ku9FJaJqTblCAeFyh8jSIYhyFlVcomA3n3BkorcrHA98j1H1XQHYjusRkE/2Wdn6+mGUYy8mJPQN3Y+pMce3Vyq2V3wiHeMgJAdebG9AlszCTVBBGNRgpF2K65GRw1XVlFEDDvadffHxw6WtsV8R2JIrlVR7tz0ZxY3ERnClhqNTNVGmzbACc3xthYvm0cbkampz/uc6MsPFw0+rKL4xES9FAf9IPqkhVZHjcWXC7PQbQ3vRJDzgiUwDVerul9Vve5TZvTW09pISEqQ9P0NfbB1+7Dy5XOqwWExjOdMhQ3QO2rLS0NTUtbTYn94TDlmJHZmYGysvLwZqsmJmbxfT0FLyebHhyvf8s0sNAEBzLcS+bGpvx+tULTIyPwe/3w+l0YnNzAxGe+tDegYHBxxgvaDMMJNQHusUwsQysiZ0Al2KzYTEYhCs9C3x4Hfn5+cZ0GxubyMk9iaXlZTjMDMT+mxUH2QWaW0CEdHTc0Y5ZbbBYrGAYhoJsgOVY2O0u3RtIoghxh4dFlciboi6YFRGspvwFYgQhFhBi2wZ7OLRmsCuybJz50DpisS0IkhxIsBa0zA9BZTj9PR4yMk9/WDg66Coi+L/CHwEGAFo4VdyrMS/CAAAAAElFTkSuQmCC"); }  /* type 0 -- booked at the counter */
.vcl-cal .cal-ico-callin { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACs0lEQVR42oxTS0hUYRT+/sedl+lESyMniixoFRqBgq4MF5E2ESRKJBlUFGnhqlVgUBQErYqaUkednRQ9KIUpiWrAWSgS+AitSCtRzHRsHv+9f+feO0KLoA4cDve753z3nO+cy8ba6rF7chAs+wvRtkG9+COF1q4wM40ARDaFaOtAHjvEtPThfWkNhKXAoGGb1JxpmDnAtMAZMDM7C1hKM5UmzPwDMylPgfJhcckYXJOhNw9hXroHqTKAEJiemwfO3SF+bhNRhnSxs7dhCQOhjhZwaQB5CvZ1K7SvLoy1vn4kUwRQ3d5CQFGHghKGVwijWF4A+BvCSD/qh/AQlG+BXb0b1e0tTfhMcxpSEDGHqS1Y2i3kdpbWzvOWoB/XI33weLyU5ryhEVeXnCRfLo0AtTw+nEABxSKtUEjuN7Pw5hQ2mNIh9K6Z8JMHUq5LK5d1mHweiXD9QSwsLGBkdBSVFZXYWboLuazCg0g39l1j2L8D+JYux1w2CcHzHSpS2jZBAkZ7epzISaBtJaXo7LqP3lg3PD76MglyrPoiSkJJ2g56JI1kEIO0lEvAaabNxcWwaNiV5Qymg1FUXOmyN+fqZWvCi1C7pwFCxhoTH0hLiQZprnfAhRMtuofCoBcsZCF25hZ+Zl7Yb0lHWizfhLHvMdSWHQc3Oo8mJkh3le9ASpcgk8kg/vwdEQGLmQkkPz3Fuln6MRidwNh8Jw6Un8Db8chhaZLSLoF04uTUhJucAJb1CDxBdwTarHOwrl7Ak7EI1CriEprhb9ZU1YjL3b0QzCWwi5uPOCeBj1+AodcYeNmB0+z8hfZX0jCqTaWc38M+McElvDIAQ/hoIwa45cdQ0Uk0NwJTk1Qcd4pP1d3EjE2+nXwj/mE1N5CsqgDizxCn4ha72Dll/L+V5eMS+fQ6+FuAAQDSCSCkzhPVPgAAAABJRU5ErkJggg=="); }  /* type 1 -- an Innkalling (recall) */
.vcl-cal .cal-ico-internet { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACwUlEQVR42pxTS08TURT+pjMtlDKUtqEUgcpCxLjwEYKSGExFRRNJFBXTH2DQGJUYIsE0MT5ADLJyhYms3BgSnsEHRWGFLoSIrQWRhPA2KbTQxwwtnel4p6alpq48yd3c853vfud851KSJIGiKMQjv/kDGxHpl+RckSQFFJQCNMkztNjF0JGrS7YTgTg2VptMsKvZbhYk5cKF8ne4YZmCoJjD0mYuVoKjGHPU4uPEXaiU/O5lm2UxhaCgZYgVJMp/s6oC9WUOzPm1mPxVgohiDDM+F4qM1+BYrEDfSDtUDJ8lK5FrFXE5ohjt3Lu7AJdK0jHP+SEo28FqH8BgqEGx6TreunqgZ1dw/OBTgmU643UJgqgk1ZYVm7EWAmY9pxCQxoG0Dsx6D5E2KjG/XoM+5wAOF/dCAlMbr2MS0wOFUFSJRT/gjQBKnQsBMUJeHYZWDWRnOLHgKcRm2ECQIlIUADTUmh44PCLpf5X0HoklGTLfIVc3vMEiopLHm+lW0CSXQiDbxapHMenmMeM2YWm9XBYFLqxFIKSHL0QjuK3A1Golwe4oSLiQ19RlzzLvP63LySFaGPJKGPvyXsCUNQJdxjjc/iJCwKD/0yNE3eLw8pPLVX+5sNHb3sB5l7ER9JE+OaxxEuw/bmPA2Yocdgt7jNPgeBaC34j17mcNKQrkIWiOVJ/Vnb81iEwDQul68JIKfDgDdywWfJs6B+fPi+AGH1fzXwbey86nbCIJFW3IL2XrntsiGtOZ+rUexjHpxPDJNkEVXB3iO+taRM/yBMFt/3OVkwZrdrQdbTXqs60kCbfH9/pA4+d75F5e4WjyX4jtQZPtvkRBZvuT3RIYsMT/XOuxGHDrld1a3/jQqmaEGDsBy+jYCGIEM67v2NlICpxAI1LI42tHf+wuM02Cy+WChhGJhVKy2kQLpfiPILUTvwUYAIfbRZew/I8KAAAAAElFTkSuQmCC"); }  /* type 2 -- booked online */
.vcl-cal .cal-ico-cancel { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADD0lEQVQ4EU1TXUiaYRT+fvQrb7wxI3/6sV9DKQTdGnNa2s2SaMWKede6G4uFM9wIYjG6EGJFMmR32xx0ty5isYVrhqaZzSAIR34WSmXswqS6KE39dl7hcx54eeV7n/M85zzniDMMg5WH3+9Xulyfh7xen/rs7EyE4xgjkUhP9d3doaejo1/VanW0HI+zBJeXlzyr1fp6dXXV9B/AYIifIIAFbsAyIyMjy3a7/R2Px8siXJEAJQ8PD3+IRqNK9JEkyXxXV9dvubyNLhQYPBKJyHd2dlSFQoHAoaTOzs7Q0tLSCyC5RazY+Pjzt62trWH2OJ1OM/pefoC8cXBw8AuLmZqamkTvpMlkUiwuOl6BKlIunnA4rJFKpdG2trYEqgiFQCBI9/f3/9ja2rqXTqeFQKjo6elxk0Kh8Fk8Hm+nKCpvsVgW9vb27kCZlNfrNUokEhoUSyRcLjfX0dGxv7Ky8hjECIgMyefzrTc3N3xwNwRl2ZuammI+n8+A4Rh307dpFIvFdEtLS4mkqqoqFQgEtOfn59WZTIYiRSLRSw6HQ2i1Wg8YF5TJZHFEEvAHjFAJF8ZqrKmpoZubm0skYKoCVQ0ecAlIRn3j0CY6xdDr9RszMzO2ioqKHJRJzUGsra3p2Xe4CZSDcgnoMwm9YYeHh/IyAKbT6Tamp6dtlZWVtxwOyZ1fmJ9zu91FEsC2oxzIPSXHxsYaj46OlOCsSKPRuJHbLFF9fX28oaGBDga3e3EMp4LBYO/FxQWxu7v7ECrDDQbDN9LhcPxdX19/DP0SBwcHSlD+DhPJsSR1dXVxIIqFQqGiJ7FYTEOQBIYmMjEx8QYRpMBRQSKRUFxdXVUD8D6o7sN4UyxJbW1t/Pr6mqRpWg3KsNoENvBo4KPuge5ncZWz2Sw1Ozv7PvInchdKLW4gJO2DOg1O4+C4PJlMytEao1CpVL9sNtskmJgv/ZkQicvlskA7T9DOs+rlNzif6+vr+2Q2m53wO4/eSgQs8Pj4WObxeIZg1ppUKiUGVQaW50SpVG7D6i7D3pywWHT/A+g+at4IYUJNAAAAAElFTkSuQmCC"); }  /* type 3 -- blocked / cancelled */
.vcl-cal .cal-ico-task { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACR0lEQVR42pRTv2tUQRD+9u179+6H0ZR2Qeys/A8kBDUIQqJczkYQsbUxGn8LFlEstFJQTLhCLVJ5lxC1MZJC8YIgaNTqLilMtNGoMeHO291xdveZuxAEHd6+nfnYb/bbmV0QEdrt+v0SPXrxaj1o8Qdlmnz5eh1uuYF13gz2k+mNiXaD5hcWIYTA16M7Se2JSPWmPf5xgXEmdYP03hRZjuWG9tf1vARzYQRSN1F99glIRdAHT4HUCiDsHhrVKcbDCDh+CyZKo2v4mFMhrIzP2wTF+/tQHyvhQ513McCOHKCElxrx/J5zGYtngMyhPjQmStg6R8Kd49rIPdLs1L78oPmlnzxWaG5plWo8VzmuOcyOZaryGrvWcizXHSFY/u6KETZWrSjIIEQgQmhqwpBym1gxBE8I0OE4jmt/Rv9yQSbMIhvmsCnqxOG7W5CRacQy5CERBTLxQ9jq6YSTJPDdKdzZjI5sDvnbElcOlLlmAcqlh3g3+xZxHGFivOx8q8Yog7UuKK1c8PgEYd8NgSc8N7WBMnUUBgpOuv3y+QEm+8oqo1oKlNIu0EJhcpBgGJ1dHIeMIggZcCeZJoX3pU+gE05yBB/EYYiINaVSwOmxfqQ5QcxBzHPK+kncznEJtPFBz1WBgO9Kz7DAzSNPnT86OorKzAzXIEaxWESlUkmOoFv3eejcRfpj3ZdB/2JDZx3HK0Dbg5q6tOEd/cWo1YVGozF98sz5XZor73FKrs1GsyWUXCvVbE6vvQV+fdvZ78T/2TfmVn8LMADdI1EN0yh6ywAAAABJRU5ErkJggg=="); }  /* type 4 -- a task, not a treatment */
.vcl-cal .cal-ico-check { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACFUlEQVR42qRTPWgUQRT+5md/7uIlMYKCQkCCYECChSBJUImNWFwjeHAWUQJBUAQFU1komNikTBcSQcXIqZVNsEoV1FiopcUZiCCIEQThcpfd2fHN7O1mvEIU3zLLvuF93/vezzKtNf7HpHmdvfV0tbmtRlSS/BVIcI7QFz+WZyq7LYEBr8xWoVsN/EkPMw9jgB9ibOpJb65AKZNZ4/PXTaQVsQ4gHS4hmQ/BOPYeCKGSNBWHhRpLwCnIBHIhnENAGaLo92DtWw3nX/Y5tG0FeUOkRGIl6DxIEGEoduHVl0dY/HAVJbZTjkNgHIFiUEKcxCQvgpmOURRKAm88xPzbSRRI71acpXNKsI0hu1grYrPxiYi6EQZdKBV6sFKfw/zrCRQoPmkB14cXU7h2CdpyWARMLx/F98Y6urtCLKxewOO1GwgoNibwldEHODkw0c6vd0rIHE+lJDMvhjDUX8ab+jMEJogLTJ66j9FD44hUi0A+4CrInNODFSTbNNZWE+/qzyGJ0NcMd899xInD44h1RFPSVnPW5nSMOt3Ay2dqqBy/g2iL7poaHl1PV+vY3zdAzVVWq9lC13jntpVHbuNaeQmMun3v0jr27TlohyQFg+9JeJ4Hd9VsD6T4nefYYBULdNw0vCNXhrEEPtcbYzeX+mNaaZ1PhbWbq/MmZxPzCBx4/Kf1zTxpD47Qd/CPf7Ii7PtfAgwA08mzInNeROkAAAAASUVORK5CYII="); }  /* Bekreftet -- the customer confirmed */
.vcl-cal .cal-ico-sms { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACQUlEQVR42pxSTWsTURQ972PyHTNJC1HQYO2qVlCrtJKNUD8IGkzBjT/BtVs3xZ1bXfkHurGigi1BobbgQjFFsELViFRLF0rQQmqLycy73jfTtKnYLrzwmHeZ+84999wjnkxXH5ExFYDQHduZgJTycflSaQz/CO15XqU4PIR4REIJfkj2McE3gE8Cv9s+XtXeVLBLaDI+HEko3FpB2hHIphRyCQduQiMVj+D2RcAYg10B7E9JBlzPjzVymRgDKLgxDa0kBHnwjNgbwOdDiRRkrwtyk2hqieVmG43GBm6Sg5aJ7Q3Q9oB4xsG9AzdQwBI8np2yrIHLGryUOA2FxfsllkdtiisghBVXrWqf1Wp5hL7oCvabZeRHr/PQYRG6NrEzt6nA4vMJNwQgialreXyZYSF8gebbKUil/qoPAcgQi+ojfbzMGyNI39ilaYxPXoClaBvpaAZPaz9Rq2/Aibl4Nr+K+fo6ah/X8Zq/iv933KItmoUYG7gD+nY3cJCKpFA5dyzoan1xZdTewyEMN2TvhIbpiCh46B+N79gXDG+gnCSmX9TRk0sGlIsnCpia+4BcNhF4YmTw4JZXtc8MhJA4dLgP9IkCZKmTKJ8fCoTqdLpscxtWA8/sZMBex9elz+jrCBZJozr7Djk3geFT/ajOLKCH/WGbGjbdyMkj3VYmRKMOBgaPorXAHa36TL109cxWUXjfXCOFLJlmwFz/Wlubm3zw8KwVrNibx/vZCZ57d+tu71UyIdm0lf18XPxf+H8EGACHM/OwlwgTzgAAAABJRU5ErkJggg=="); }  /* an SMS went out */
.vcl-cal .cal-ico-smscheck { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACdUlEQVR42oxTS2gTURQ9781MJplMamyz8YMggqDgQsVY6xelKSI1dqMgiBuRFgSl1M/CjeKiSii6ExculYK2Mam20LSVYG390OJGakHRQhWrLhqJSZOZN943Y7WUKD6473vPeffc+x5zHAeMMSxu6Ye93UKIQ3LOOU82HtjftNhHYr2uQutKphwhbDLhdD9IV3SSWFVOkqmeJDnGaQuMTHLaVhnFYhGfPs/Q3AKRdDbFG48sJnEJLNuO79oeRUDlUJhHQD3GRkfwbTaHHXW1eDL64jC5ViYQtg0dAqsufURIZ6gOaagOalgaXA1TVxG1SiA5FaWqnhbbNcPUEIkYiIR9qAkoCPsYFEGyRBGOqJwr7kYgBATpVEJB6DUmCmYVJoSBwa8c6fcWGGnijvr3CGxboGQ5KHAd02UdMIJgFIFt5VHMK3RLGaqz5N8ERUvBzNkV8DJYAhhpZiadmmjvOoq+t3dR3w5HUwik4GaqFS1/CEifYAqO3zBd1lPbsrAVBvPESdzbPIl3O3M41gjUrTyHZ9PXMDqO5oMdrmvL7xzIwrXtHXNN9fng03QonT3IrM2hdgPw/QewLnwVs3lg03p5KZoXJJHC5Q6eDg9heHgQmqbCr6p4ns2C09HWZW04E/WqcJrGLcvPY74o6nwEnP5DfUMD5L58eZwkxWIx3L4DDE4m0Ps6gYv7HFwZYDA00INbUEb5pnW/H0MDGbJ+BA0DASOA/swjbAztwcsJyicxj3y4AIXGcVrP5XBfYpkEJzquPw6a5u750ng5YZRtH3RehVeFW3gz1+eScOaB+y+jlbBT7Nd3XkOIMP6/fSGbktifAgwARTcWiatybhMAAAAASUVORK5CYII="); }  /* ...and was delivered */
.vcl-cal .cal-ico-smserror { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACbElEQVR42oxTTWgTQRT+ZvYv2d1obFM8CIJ4EE96aq1WLWoJRWpahHrUg0orPYkiePMuoojg2YMoNTZpFEXbRpPWVpQgPfVWW4jFNq1WShuS3VnfZPsjIYoDbxlm3vv2fe/7hnmeB8YYqlfq5asBIUSn3HPOEx2n27uqc2St/6mxnicGPSFcCuENJFM1k2StKjeJwRcJSozRERiFxHSdMorFIua+z9PeAYE87Yp1nKsGqQA4rhs7dqQRQZVDYT4AfZGbGMfi8i+0HD6E0YlP3ZRaG0C4LgwI7L71DSGDoS6koc7SsMPaA9tQ0eiUQHRqUlV9Lm4lTFtDJGIiEtZRH1QQ1hkUQbREkQBqz4pXOhACgngqIQtGvY01exumhImRAkdqxpG/gFCUv3fgugIlx8MaN5AvG4BpgVEHrrOC1SKHo1EEzH8DFB0F89d3wZ9gCWDEmdl0G0LmRh/M/kd4tn+nxzQdiqo+7MxN924BED/BFJy/Z1dQ+5ozIDwYVy6isPoDoQYLZy9foIMASebgY3a8J+E30Ls5A8nz2olcJVRdh64Tlf4UllcKaG46CPxcgJefBhbn0HRgn/RGz5aMUiLu4cNYuqJ/y9FW6V9ks+/RIKd/8gxwqhubhh+Jw0sO/eED6oDTe2iLRiHFks6TANG2KL7cpPsnDyAe3ye/UB6pwWkObF0VvuFpIxBAeniI4i0s04QZDCL95jWc1naMTX4l7mVoinSpg9GpPJYE4rKWyeLbd+6+s2z7+EaH/kwYmKbBC1rYPpyE9TkDT6GGOcNS2Y1fmpy5SrWzbP0576WKMP5/LVDMytrfAgwAXFMelZ583jwAAAAASUVORK5CYII="); }  /* ...and was not */
.vcl-cal .cal-ico-lock { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACU0lEQVR42oxTW2sTQRg9szcNXiCWUrQPxVsSq91Y+2J98EER/BVSRSqpRUqpbz7qi0oopW3wgor/QHwRH3xuGyq6WsnGakHQtpY2qGmyu7Oz67ebi4mN4AfLzM4353zfnDPDfN9HEIwx1OLho6fTgJ+ir7oS5Fjm8qULQ7U9ddzfBFOZ+18Tifi+2OGDtIlBVRVwzvFx8RNyOfPb1dRg5z8JxicmpxOxWKq9owPGWwMbhUKY2xONQk/qWFtdRS6fz4xcGx6q4RQ0hG07KUXTMDObhZnPT02kbz8O1kdGrw+ULXs4FjtE3Qg6GupHkRoJNi0B15NQopHAN2lpPvjG03duLX/fCHNM2dYIaSb4YUngLIKfjhr8rjSkVuo5W2siqGuQS3cZTJR6IDh8JkFAo1EO5KIqHiSf01SAySo8OfLqyOiXs00alLno6b0yCajbK7YRCaSqtR4V8b1gAjgWXt8bO1PD1Qkcx4O1vgjz2Q3CKmSfjKVlN8zt3xtYKeC5Lo5ffALuCGwhcIncK66hu/c0ZEXDkjmPBy8rNt4d3I243gdB1WEVwIXfgkAQq9jEByNLBCp2RDR0tlWVlmRanyN5bCQPnKNiLTrgLrE6v3A02Q9JVvCZOojuquQEadCtn4RwHeqA7ORoQRC0xamDd7NhRYWu8MD59iq5oA5m4JFDelsXHK/FEQTZyYhA7zsVOmC+z5L4tWsOHDvRTyKRkKV1NEjwh6BY9o0Xz9/oVQyBJNQeaPCoFhbmwjsRvBnbhbHlIlEiTsNO/F8UCWcGk98CDABOkBpi7KzK3wAAAABJRU5ErkJggg=="); }  /* Delbehandling -- a part treatment */
.vcl-cal .cal-ico-invoice { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB9ElEQVR42pRTS0scQRD+eh6evTh6MCeR/IG9rCgejHcxQcwmoEYIOcZTLj6irHjwkoNXibowotnsQ7yIJmAi5JwfoGTFVRcPEbO+Zma7U90OsyPjs+Cbrqqprvn66xomhMDK6upfr1KpFZxDIGwyYtAYA2PsqLurywreCFF1vuay4j4rlUoincnY4QYSmgr4VbdicQ+HB/sK+8UiCoVdlU/ZNizLQms8/jKdzS6GORrhYGRuC6ahK9/zOCYH25Qvj+B6HuobGtDW0tKbyeVMSr+INJh+9wy6pkiBkx6OW1H+0+ZmfEmnVe51IiGpP7+RgaFrUixwOhvTGEzzqlksFlOICCjZhRu8n1nDEKHGNPFp5TetNRj9vIF/J8cYX/iOvgkbEZPdSF2ltHd5Jrh3rtbL87I4Oz0J4FDsXJyqOr9e7b12hLfTeRiGpm7lSaOFDz1xJO0tDCdaMbX8Czt/DjA3kridAXEgVAguMXGE61wIz4fgDsGNMLimwUByCW8ml5Q0YwubJKqOj6kf0OlqJ1I/0Z9cvluDiHEuH/5atYgGdHM+g0X6KiMNQBrUYayvA8Oz60gOdmJ8/ht2CoeYH30V1AcMaLLEY8yvrzIol8ub2Xy+PTwg8k8Mh8ynKgdN1gd5uYmSTeTX4uF2TPu2pfNfgAEAnJq33j2X3/sAAAAASUVORK5CYII="); }  /* Fakturert */
.vcl-cal .cal-ico-order { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACpUlEQVR42oxTW0hUURRd5z56iKITOlZYKTVCoYE5CT7zq36t8EO/7KfCikkwyAIhin76EKEXhiWmYymKJb0oIVHDSrFiTNTEmcHURmO0ZlJn5t7duXfSmSKixV3cu7mLtTfrnM0edHS4A4oSQ6oKQji0ikFgDIyxWVEUjUwQoEqEHYNRUAUVvoWvkAKBQMyhggL8Cy6XK667t9cqyXLxn/8EomDfCbuD0wm7w6m/tVpDg9UKo9GInKysIt7Mqup6CjP49SFJImQ5RK3WoI09OzeH+Ph45OVkF5FKNxmBGIKdhRU3QZQgcgqCDEkMUkOyyYTnLzrRdL8ZcbFxeNPXfyxiVxK+R0ZxrUDSyjSypBkIPDSBhyaB8R6Liz7sTU/XqaH83AVYTp1EXeMdmN17EOt2aRMEsUZeh0n7NKacM5hxurC7MhJKgGHe44N7Cegt24LzJSmorb+HDHMmngqdyLhx3IyW1lbS8MProyQLaPzjNJnKQKqfaNFHdOVqHfWUJxO9yidqTqTh9otUdrZSmzt9sLQWUviRdJSMYn/NJjwsGYefS4jHkGm/jOzDW4HJaWDhG/reWlFVM2y2VFQMYFYJGTB+HjtTTfhURXosfs7+09uQW5gIOKcA9xdcf70RCcW38LhwaQBQMMnUUAayJOPDOxts70dgszmw3JKH3DMngOh8YM6J6h4DSm8PQVlagOJbhuIPAD4lZCBKDAfrUxGxNhqFjYloG0rAo5ceHuR6VHdvgOXumK5TA8v8BqjQrwF/+HmxsAxGcIBn8OToZ1y7dAQRIxPYPKrC0uQIbQj9vjESvwyrRUpaMsbSgoKqhmd/3YtwvW7g9Xi62trb9wWd9c1b3cTgTtJqV20zvV5vl8FgWDXQVNs5Y/D/mOccXyl+CjAA67Ukf2+zLCMAAAAASUVORK5CYII="); }  /* Leveres -- there is an order */
.vcl-cal .cal-ico-car { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACFklEQVR42qRTTWsTURQ985GxjYY2MRmTtlRR3FYEoaCiIOiuK39AaRd12aIr079g/YC6sGhrFyK4ErNTKbR00YAubNGiIIGm08RJKmnSaRIzb653Jn5MuqhgLlxm3nvnnXvuu/dKRIR2TEabJvkXEzdvDYqGPUNkD2C/MkmCLKurihoYu3f3Tvr3turHxHv7V85cu46QfgzkuOwEmUPUbEJHIIBC5vNAOvVsZX9gz24nk1NbdaKNPaLsLpHBnreIirzervF/tXn2catCycnJqT/CHj956mndNLLoO34CkWgMweAR1H/UWwJo2iHUqha+FwrY3Migr6ef9RHUtU/rC0cj4SsBVcP66hqEEEgk4igy0G/RWAy5XB6KqqKzM4isYaBU3llwczn5aP7Fcj6bSTh2DY7jYHhkFDFd96VKKJgm5udm+SG5cIqGiN6TG78xfNFD0P0I7Y1vg98NRbMCayePN6lUE8zmkl4dGsLhrjiiesirffBBGNJESVJ/IdDgT9oAujtCsMwPOHvhPFeuSUBcEtP8hq7oaXxhzGAvb4rG3z54+XqZFFGFTLZbbnYFktzaY8RBiITXHg6fk6yhXC6FPQVvXz3/ry6cnn5Y8Qh2rTpHlTxBc7Mz5w66NDI69s6dHzctNxGPQDhiSVGVS45tL/Ly/UEEQjQWZVW57NhiyT8Lp9i72UvsX/+hvAUrtTvOPwUYAB8D6/6gBekDAAAAAElFTkSuQmCC"); }  /* the car line */
.vcl-cal .cal-ico-user { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACkUlEQVQ4EW2Sv2/TUBDH79mO46RO0+D0V1QSKtQAqkAwVIA6dAIVsSCQGPkXunRhBLGBkJgQA2PVDYGQUBFCFSyIVrRiKT8KZGmTJqnjxPVv+73Hs9VGweGks5/vfe+j890hSinE7e68MpORxYKUSiDTxlRTnVD0/vGqasa1KA5YmMudyA0lnmbk5HxSEsGwMDQblu95+Lrnkk/P1jSjFyL0fhyeXxXGB84WCnlAvAyq5kFbrycsq/2G3d9k/ro3pw+QEJBAKY8ISEwnA6YY+IQFmOhCgDHqTQ7PfQA/8GFwuAAjpZMgpbNQ4kQolSfgxVIdHNuJ5/cDPNt1xiYncfF0macBAWDtS8sCiAJxOOLiOIGLBw5M/2NuJL8nZ9LAsVvTMMDQdQKIfvCw34jr+wBLW/DE97mvzeo+6WgdaGltWm1oviqPP1r+Tr/FAf+Mce7hFne5rKBpffm5As3bSWc3tbNv4M3moMol0rO1qcXKXtsmq4tnusvTrYAlH2f0l8w3hybO3RjKpqTANwG7Jjeq5JT87MJbdveO+S3mXYsqmHuwcUnJiPevnh++qGQH5KljDjcqtIBaKmi6AzV3AJzMKWgZ2Fr/1fqxtq3dYQT1872ZWjRGAZECD+TKWCYBqkdgS0uBOF6CqWIRqO7ClwoBz+YhK0KaYnzBcT2FAdywjAjA9hTCEVc7Afzu+OCzP7RMAbQ8AtUMYHOHQkB9KGYQtA5cNhk7zI0sAjRaB9iwRGfjT1vSbAzhutWqCFbYOzyHjfIJBV3iYbdpuLbFRABsSQ4rqNfqOgW0vV2pl6P2ske3zUx0tL+IkTgaVHjq6ywcrWXURKl8Lc8C04TSwaPM/wFCEkJgMs460/v2zxXnLw06NpuxS5nrAAAAAElFTkSuQmCC"); }  /* the customer line */
.vcl-cal .cal-ico-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4EWP8//8/AyWABadmkMHNzfFg+drahQyMjNiVglyAFS9aZAc04g8YL1pki1UNyBKsEh8/sv+XlLwMlgYpkZS89P/jRzZsarEbkJFRDtcMMgCEMzLKiDPg6FHF/ywsX8CaQDbDXAISO3pUAd0QVBf8/s3439BwE9z2yZPT/oMwzBUgOZAapHBjQgnaKVOCGM6f94GLsbP/ZwBhGADJTZkSCOOCaIQBDx7wMdTXTwCK4YgvkHKgHEjNkyd8YB6QQBhQVNTI8OmTDEwCTP/8ycgAwsjg0ydZhoKCBrgQ2D+bNhkB3fkL7leYn0EBCAtEmBiE/vV/0yZDkF6G/58/s/xXUjqBoRmkUETkBhijaoZEq5LScZBe5gZW1lSGDRsy4E5CZtjbr2SQlr7PcOeOKbIwmP3+vQwDK+tThv9qavuw2o7NVnQxNbU9jP9XrLBh2Lw5CWgqG4Yt+AV+MQQHzwMnCvzq8MsCANZ7b8nDw/ghAAAAAElFTkSuQmCC"); }  /* the model is flagged NB */
/* === END appointment icons === */

/* TDBCheckBox */
/* v055: TDBCheckBox — checked when the bound field value matches valueChecked (the {DataFieldChecked} token; the
   live feed sets .checked). Display-only (pointer-events:none) — the runtime write-back (Post) is a later step. */
.vcl-dbcheckbox {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: 13px Tahoma, "Segoe UI", sans-serif;
  color: #000;
  user-select: none;
}
.vcl-dbcheckbox-input {
  pointer-events: none;   /* display-only */
  margin: 0;
  flex: 0 0 auto;
}
.vcl-dbcheckbox-cap {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* TDBComboBox */
/* v056: TDBComboBox — a dropdown of its `items`; the option equal to the bound field value is selected (the
   {DataFieldOptions} token; the live feed sets select.value). Display-only (pointer-events:none) — the runtime
   write-back (Post on change) is a later step. */
.vcl-dbcombobox {
  box-sizing: border-box;
  display: inline-flex;
  align-items: stretch;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbcombobox-select {
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: inherit;
  color: inherit;
  padding: 1px 3px;
  pointer-events: none;   /* display-only */
}

/* TDBDateEdit */
/* v079: TDBDateEdit — a data-aware date/time edit (the native-date-input sibling of TDBEdit). `kind` picks the
   input type: date -> <input type=date>, time -> <input type=time>, datetime-local -> <input type=datetime-local>.
   The value is rendered EMPTY at /design and hydrated at runtime by the shared feed (which ships date/time fields
   as ISO yyyy-mm-ddThh:nn:ss; the client slices the part this input needs — see TMySQLQuery/client.js). Display-only
   in browse mode (readonly attr); edit mode unlocks it and the picked value is written back on Post (PostRow parses
   the ISO value into the date column, locale-independent). Modeled on TDBEdit. */
.vcl-dbdateedit {
  box-sizing: border-box;
  display: inline-flex;
  align-items: stretch;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the inline {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbdateedit-input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;     /* inherit the wrapper's font (model fontFamily/fontSize/bold/...) */
  color: inherit;    /* and the model text colour */
  padding: 1px 4px;
}

/* TDBEdit */
/* v049: TDBEdit — a data-aware edit box. On /design its value is the LIVE bound field value (the renderer's
   {DataFieldValue} token, resolved through designDbUnit), or a muted [fieldName] placeholder when the query
   isn't open. The inner input is readonly here (design-time display); the runtime data feed lands in a later
   version. Geometry/surface come from the {Style} chain; this gives the default edit look. */
.vcl-dbedit {
  box-sizing: border-box;
  display: inline-flex;
  align-items: stretch;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the inline {Style} FontCss overrides it when the model sets font */
  color: #000;
}
.vcl-dbedit-input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;     /* inherit the wrapper's font (model fontFamily/fontSize/bold/...) */
  color: inherit;    /* and the model text colour */
  padding: 1px 4px;
}
/* the [field] placeholder (no live data yet) reads as muted italic, so it's clearly not real data. This matches
   the `value` ATTRIBUTE; refreshDbControls (v433/tsw2 v363) syncs that attribute when it binds a real value, so live
   rows stop matching this selector and render with the model's own font/colour. */
.vcl-dbedit-input[value^="["] { color: #999; font-style: italic; }

/* TDBForum */
/* TDBForum — a simple community forum (v381). A DB-aware control like TDBCalendar: it binds to two
   TMySQLQuery components (topicsQuery/postsQuery) and renders a sections rail + topic list + thread +
   composer. All colours/font come from props, applied by client.js as --tpf-* custom properties on the
   root element (so both the light template default and the prop-driven runtime share one stylesheet).
   v381 (shell): chrome + design mock; live data lands in v2. */
.vcl-forum {
  --tpf-bg: #ffffff; --tpf-header: #3b4a6b; --tpf-accent: #2d6cdf;
  --tpf-text: #1f2430; --tpf-border: #e2e6ee; --tpf-size: 14px;
  overflow: hidden; box-sizing: border-box; display: flex; flex-direction: column;
  background: var(--tpf-bg); color: var(--tpf-text); border: 1px solid var(--tpf-border);
  border-radius: 10px; font-size: var(--tpf-size); line-height: 1.4;
}
.vcl-forum * { box-sizing: border-box; }

/* the static SSR placeholder (replaced by client.js) */
.tp-forum-ph {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px;
  background: linear-gradient(160deg, #3b4a6b 0%, #232c42 100%); color: #fff; text-align: center;
}
.tp-forum-ph-head { font-size: 22px; font-weight: 700; }
.tp-forum-ph-sub  { font-size: 11px; opacity: .7; }
/* v476: design-time placeholder text moved out of template.html (it used to ship into every exported
   page) into CSS-generated content scoped to the design canvas. See TScrollStory/styles.css. */
.tp-design .tp-forum-ph-head::before { content: 'Forum'; }
.tp-design .tp-forum-ph-sub::before  { content: 'community forum \2014  renders live in the running app'; }

/* header bar */
.tp-forum-head {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; background: var(--tpf-header); color: #fff;
}
.tp-forum-title { font-weight: 700; font-size: 1.15em; }
.tp-forum-spacer { flex: 1 1 auto; }
.tp-forum-btn {
  font: inherit; font-weight: 600; cursor: pointer; color: #fff;
  background: var(--tpf-accent); border: 0; border-radius: 6px; padding: 6px 14px;
}
.tp-forum-btn:hover { filter: brightness(1.08); }
.tp-forum-btn[disabled] { opacity: .5; cursor: default; }

/* body: sections rail + main pane */
.tp-forum-body { flex: 1 1 auto; display: flex; min-height: 0; }
.tp-forum-rail {
  flex: 0 0 180px; border-right: 1px solid var(--tpf-border);
  padding: 8px; overflow: auto; display: flex; flex-direction: column; gap: 2px;
}
.tp-forum-rail-h {
  font-size: .78em; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  opacity: .55; padding: 4px 8px 6px;
}
.tp-forum-sec {
  text-align: left; font: inherit; cursor: pointer; color: var(--tpf-text);
  background: transparent; border: 0; border-radius: 6px; padding: 7px 10px; width: 100%;
}
.tp-forum-sec:hover { background: color-mix(in srgb, var(--tpf-accent) 12%, transparent); }
.tp-forum-sec.tp-on { background: color-mix(in srgb, var(--tpf-accent) 18%, transparent); font-weight: 600; }

.tp-forum-main { flex: 1 1 auto; overflow: auto; padding: 12px 14px; min-width: 0; }

/* topic list */
.tp-forum-list { display: flex; flex-direction: column; gap: 8px; }
.tp-forum-topic {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  border: 1px solid var(--tpf-border); border-radius: 8px; padding: 10px 12px;
}
.tp-forum-topic:hover { border-color: var(--tpf-accent); }
.tp-forum-topic-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tp-forum-topic-title { font-weight: 600; min-width: 0; }
/* v391: explicit "Started by <author>" line under the topic title */
.tp-forum-topic-by { font-size: .8em; opacity: .6; }
.tp-forum-topic-author { font-weight: 700; opacity: .95; }
.tp-forum-topic-meta { font-size: .82em; opacity: .6; white-space: nowrap; }

/* v390/v392: admin moderation controls (pin + delete) — on a topic row and in a post head */
.tp-forum-topic-actions { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; flex: 0 0 auto; }
.tp-forum-pin {
  font: inherit; font-size: .82em; cursor: pointer; color: var(--tpf-accent);
  background: transparent; border: 1px solid var(--tpf-border); border-radius: 6px; padding: 2px 8px;
}
.tp-forum-pin:hover { border-color: var(--tpf-accent); }
.tp-forum-pin.tp-on { background: color-mix(in srgb, var(--tpf-accent) 14%, transparent); font-weight: 600; }
.tp-forum-pin-badge { color: var(--tpf-accent); }
.tp-forum-del { display: inline-flex; align-items: center; gap: 4px; }
.tp-forum-del-btn {
  font: inherit; font-size: .82em; cursor: pointer; color: #c0392b;
  background: transparent; border: 1px solid var(--tpf-border); border-radius: 6px; padding: 2px 8px;
}
.tp-forum-del-btn:hover { border-color: #c0392b; background: color-mix(in srgb, #c0392b 8%, transparent); }
.tp-forum-del-q { font-size: .82em; color: #c0392b; font-weight: 600; }
.tp-forum-del-yes, .tp-forum-del-no {
  font: inherit; font-size: .82em; cursor: pointer; color: var(--tpf-text);
  background: transparent; border: 1px solid var(--tpf-border); border-radius: 6px; padding: 2px 8px;
}
.tp-forum-del-yes { color: #fff; background: #c0392b; border-color: #c0392b; }
.tp-forum-del-yes[disabled], .tp-forum-del-no[disabled] { opacity: .5; cursor: default; }

/* thread view */
.tp-forum-thread { display: flex; flex-direction: column; gap: 10px; }
.tp-forum-back {
  align-self: flex-start; font: inherit; cursor: pointer; color: var(--tpf-accent);
  background: transparent; border: 0; padding: 2px 0; font-weight: 600;
}
.tp-forum-thread-title { font-size: 1.3em; font-weight: 700; }
.tp-forum-post {
  border: 1px solid var(--tpf-border); border-radius: 8px; padding: 10px 12px;
}
.tp-forum-post-head { font-size: .82em; opacity: .65; margin-bottom: 4px; }
.tp-forum-post-author { font-weight: 700; opacity: .95; }
.tp-forum-post-body { white-space: pre-wrap; word-break: break-word; }
.tp-forum-post-edit {
  font: inherit; cursor: pointer; background: transparent; border: 0; padding: 0;
  color: var(--tpf-accent); font-weight: 600;
}
.tp-forum-post-edit:hover { text-decoration: underline; }
.tp-forum-post-editor { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.tp-forum-post-editor .tp-forum-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* composer + empty state */
.tp-forum-composer {
  flex: 0 0 auto; border-top: 1px solid var(--tpf-border); padding: 10px 14px;
  display: flex; gap: 8px; align-items: flex-end;
}
.tp-forum-input, .tp-forum-area {
  font: inherit; color: var(--tpf-text); background: var(--tpf-bg);
  border: 1px solid var(--tpf-border); border-radius: 6px; padding: 8px 10px; width: 100%;
}
.tp-forum-area { resize: vertical; min-height: 40px; }
.tp-forum-empty { opacity: .55; text-align: center; padding: 28px 12px; font-style: italic; }
.tp-forum-note {
  margin-top: 10px; font-size: .8em; opacity: .5; text-align: center;
}

/* v383 writes: new-topic composer + reply box */
.tp-forum-compose { display: flex; flex-direction: column; gap: 8px; max-width: 640px; }
.tp-forum-compose-h { font-size: 1.15em; font-weight: 700; margin-bottom: 2px; }
.tp-forum-compose-actions { display: flex; gap: 8px; }
.tp-forum-btn-ghost { background: transparent; color: var(--tpf-accent); border: 1px solid var(--tpf-border); }
.tp-forum-btn-ghost:hover { filter: none; background: color-mix(in srgb, var(--tpf-accent) 10%, transparent); }
.tp-forum-replybox {
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--tpf-border);
  display: flex; flex-direction: column; gap: 8px;
}
.tp-forum-replybox .tp-forum-actions { display: flex; justify-content: flex-end; }
.tp-forum-signin { margin-top: 12px; padding: 10px; text-align: center; opacity: .6; font-size: .9em;
  border-top: 1px solid var(--tpf-border); }
.tp-forum-err { color: #c0392b; font-size: .85em; }

/* v385 attachments: the composer/reply "📎 Attach" bar + pending-file chips */
.tp-forum-attachbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.tp-forum-attach-btn { padding: 5px 12px; font-size: .9em; }
.tp-forum-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tp-forum-chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: .82em;
  background: color-mix(in srgb, var(--tpf-accent) 10%, transparent);
  border: 1px solid var(--tpf-border); border-radius: 999px; padding: 3px 6px 3px 10px;
}
.tp-forum-chip-x {
  font: inherit; line-height: 1; cursor: pointer; color: inherit; opacity: .6;
  background: transparent; border: 0; padding: 0 2px; font-size: 1.1em;
}
.tp-forum-chip-x:hover { opacity: 1; color: #c0392b; }

/* v385 attachments: rendered under a post in the thread */
.tp-forum-atts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.tp-forum-att-imglink { display: inline-block; line-height: 0; border: 1px solid var(--tpf-border); border-radius: 8px; overflow: hidden; }
.tp-forum-att-img { display: block; max-width: 220px; max-height: 220px; width: auto; height: auto; object-fit: contain; background: color-mix(in srgb, var(--tpf-text) 5%, transparent); }
.tp-forum-att-chip {
  display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .85em; cursor: pointer;
  color: var(--tpf-text); background: transparent; border: 1px solid var(--tpf-border); border-radius: 8px; padding: 6px 12px;
}
.tp-forum-att-chip:hover { border-color: var(--tpf-accent); }
.tp-forum-att-chip[disabled] { opacity: .5; cursor: default; }
.tp-forum-att-ico { opacity: .7; }
.tp-forum-att-name { font-weight: 600; }
.tp-forum-att-size { opacity: .55; font-size: .9em; }

/* TDBGrid */
/* TDBGrid — hand-rolled data grid (Stage 1: read-only). Look matches the other TDB* controls
   (Tahoma 13, #7a7a7a frame, #0a64c0 selection). The host geometry comes from the inline {Style};
   layout/structure comes from here. client.js rebuilds the body at runtime (virtualized); on /design
   (no client.js) the static template skeleton below is what you see. */

/* v099: UX overlays — a busy spinner (top-right, during paging/detail fetches) and a centred empty state */
.vcl-dbgrid-spinner {
  position: absolute; top: 5px; right: 7px; z-index: 8;
  width: 15px; height: 15px; box-sizing: border-box;
  border: 2px solid #c4d2e6; border-top-color: #0a64c0; border-radius: 50%;
  animation: vcl-dbgrid-spin 0.7s linear infinite;
}
@keyframes vcl-dbgrid-spin { to { transform: rotate(360deg); } }
/* w014: THE SLOW-FETCH OVERLAY. Centred over the grid, above the empty-state,
   and shown only after a fetch has been out for about a second -- see
   LOAD_MSG_MS. The bar is INDETERMINATE on purpose: the feed answers in one
   piece, so there is no progress to report and a bar that pretended otherwise
   would be a decoration that lies. */
.vcl-dbgrid-loading {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  z-index: 9; min-width: 220px; padding: 14px 18px;
  background: rgba(255, 255, 255, .96); border: 1px solid #c4d2e6;
  border-radius: 5px; box-shadow: 0 6px 20px rgba(12, 30, 46, .18);
  text-align: center; pointer-events: none;
}
.vcl-dbgrid-loading-msg { font-size: 13px; color: #46525f; margin-bottom: 9px; }
.vcl-dbgrid-loading-bar {
  height: 6px; border-radius: 3px; background: #e3ebf4; overflow: hidden;
}
.vcl-dbgrid-loading-bar > i {
  display: block; height: 100%; width: 38%; border-radius: 3px;
  background: #0a64c0; animation: vcl-dbgrid-slide 1.1s ease-in-out infinite;
}
@keyframes vcl-dbgrid-slide {
  0%   { margin-left: -38%; }
  100% { margin-left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .vcl-dbgrid-loading-bar > i { animation: none; width: 100%; }
}

.vcl-dbgrid-empty {
  position: absolute; left: 0; right: 0; top: 55%; transform: translateY(-50%);
  text-align: center; color: var(--tp-fg-muted, #8a93a0); font-style: italic; pointer-events: none; z-index: 4;
}

.vcl-dbgrid {
  position: relative;            /* v087: positioning context for the frozen-column overlay */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--tp-surface, #ffffff);   /* v305: theme tokens, fallbacks = pre-v305 literals */
  border: 1px solid var(--tp-border, #7a7a7a);
  font: 13px Tahoma, "Segoe UI", sans-serif;
  color: var(--tp-fg, #000000);
  user-select: none;
}
.vcl-dbgrid:focus { outline: none; }

/* v087: frozen (pinned) left columns — an opaque overlay with a right shadow; its canvas is vertically synced */
.vcl-dbgrid-frozen-head {
  background: var(--tp-surface-2, #f0f0f0);
  border-bottom: 1px solid var(--tp-border, #aaaaaa);
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.14);
  overflow: hidden;
  white-space: nowrap;
}
.vcl-dbgrid-frozen-body {
  background: var(--tp-surface, #ffffff);
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.14);
}
.vcl-dbgrid-frozen-canvas { position: relative; will-change: transform; }

/* header ------------------------------------------------------------------ */
.vcl-dbgrid-head {
  flex: 0 0 auto;
  overflow: hidden;
  background: var(--tp-surface-2, #f0f0f0);
  border-bottom: 1px solid var(--tp-border, #aaaaaa);
}
.vcl-dbgrid-head-inner { position: relative; white-space: nowrap; will-change: transform; }
.vcl-dbgrid-hcell {
  position: relative;            /* v086: anchor the resize grip */
  display: inline-block;
  box-sizing: border-box;
  /* sqlconvert w008: FONT-RELATIVE, so a grid with a bigger font gets a header
     band to put it in. It was a hard 22px while the body already followed the
     rowHeight prop, so raising the font clipped the titles and nothing else.
     1.7em is 22.1px at the .vcl-dbgrid default of 13px -- the same header every
     existing page already draws. */
  height: 1.7em;
  line-height: 1.7em;
  padding: 0 6px;
  font-weight: bold;
  vertical-align: top;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* v086: sortable header + sort arrow + drag-to-resize grip */
.vcl-dbgrid-hsort { cursor: pointer; }
.vcl-dbgrid-hsort:hover { background: var(--tp-hover, #e8eef7); }
.vcl-dbgrid-sort { font-size: 9px; color: var(--tp-accent, #0a64c0); }
.vcl-dbgrid-resize { position: absolute; top: 0; right: 0; width: 7px; height: 100%; cursor: col-resize; }
/* v093: drag-to-reorder — header text is the drag handle; the drop target shows a blue left edge */
.vcl-dbgrid-htext { cursor: grab; }
.vcl-dbgrid-drop { box-shadow: inset 3px 0 0 var(--tp-accent, #0a64c0); }

/* body (scroll viewport + absolutely-positioned virtualized rows) --------- */
/* overscroll-behavior: contain -> when the grid's own scroll reaches its top/bottom, the wheel does NOT chain out
   to the page behind it (e.g. the page scrolling under an open modal). Keeps a wheel gesture over the grid inside
   the grid. (v462 / tsw2 v393) */
.vcl-dbgrid-viewport { flex: 1 1 auto; overflow: auto; overscroll-behavior: contain; position: relative; }
.vcl-dbgrid-canvas   { position: relative; }
.vcl-dbgrid-row      { position: absolute; left: 0; white-space: nowrap; }
.vcl-dbgrid-row-alt  { background: var(--tp-grid-row-alt, #f7f9fc); }
.vcl-dbgrid-row-multisel { background: var(--tp-accent-soft, #d8e8fb); }                                     /* v092: multi-selected row */
.vcl-dbgrid-row-sel  { background: var(--tp-accent, #0a64c0); color: var(--tp-accent-fg, #ffffff); }
.vcl-dbgrid-row-sel.vcl-dbgrid-row-multisel { background: var(--tp-accent, #0a64c0); color: var(--tp-accent-fg, #ffffff); }  /* current row wins over multi-sel */
/* v092: indicator / select gutter cell */
.vcl-dbgrid-indcell { color: var(--tp-accent, #0a64c0); font-weight: bold; padding: 0 !important; overflow: hidden; }
.vcl-dbgrid-indcell input[type="checkbox"] { margin: 0; vertical-align: middle; }
/* v098: master-detail — expand chevron + indented detail rows */
.vcl-dbgrid-expcell { padding: 0 !important; }
.vcl-dbgrid-expand { cursor: pointer; color: var(--tp-accent, #0a64c0); font-weight: bold; }
.vcl-dbgrid-detail {
  position: absolute; left: 0; box-sizing: border-box;
  padding-left: 30px; background: var(--tp-tint, #f4f7fb); border-bottom: 1px solid var(--tp-grid-line, #e2e8f1);
  white-space: nowrap; overflow: hidden; color: var(--tp-fg, #2a2a2a);
}
.vcl-dbgrid-detail-head { background: var(--tp-tint, #e7eef7); font-weight: bold; }
/* v100: tree — per-level indent + expand chevron in the tree column */
.vcl-dbgrid-tree-indent { display: inline-block; height: 1px; vertical-align: middle; }
.vcl-dbgrid-treechev { display: inline-block; width: 14px; text-align: center; cursor: pointer; color: var(--tp-accent, #0a64c0); font-weight: bold; }
.vcl-dbgrid-tree-spacer { display: inline-block; width: 14px; }
.vcl-dbgrid-dcell { display: inline-block; min-width: 84px; padding: 0 8px; overflow: hidden; }
.vcl-dbgrid-dhcell { color: var(--tp-fg, #2f4a63); }
.vcl-dbgrid-dmsg { color: var(--tp-fg-muted, #6b7785); font-style: italic; }
.vcl-dbgrid-cell {
  display: inline-block;
  box-sizing: border-box;
  height: 100%;
  vertical-align: top;
  padding: 0 6px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* v096: cell renderers — 'bar' (proportional fill + value text) and 'badge' (coloured pill) */
.vcl-dbgrid-bar { position: relative; height: 14px; margin-top: 3px; background: #e9edf2; border-radius: 3px; overflow: hidden; }
.vcl-dbgrid-bar-fill { position: absolute; left: 0; top: 0; height: 100%; background: #4a90d9; }
/* v244: two identically-positioned text layers (absolute + flex-center so they overlay pixel-for-pixel). Base = dark
   (over the light track); -over = white, clipped to the fill width (over the blue fill). Auto-contrast, split mid-glyph. */
.vcl-dbgrid-bar-text { position: absolute; left: 0; top: 0; right: 0; bottom: 0; display: flex; align-items: center; z-index: 1; font-size: 11px; line-height: 1; padding: 0 4px; color: #14213a; white-space: nowrap; overflow: hidden; }
.vcl-dbgrid-bar-text-over { z-index: 2; color: #fff; }
.vcl-dbgrid-badge { display: inline-block; padding: 0 7px; border-radius: 9px; color: #ffffff; font-size: 11px; font-weight: bold; line-height: 16px; }

/* optional grid lines (toggled by client.js / set in the design template) - */
.vcl-dbgrid-collines .vcl-dbgrid-hcell { border-right: 1px solid var(--tp-grid-line, #cccccc); }
.vcl-dbgrid-collines .vcl-dbgrid-cell  { border-right: 1px solid var(--tp-grid-line, #e2e2e2); }
.vcl-dbgrid-rowlines .vcl-dbgrid-row   { border-bottom: 1px solid var(--tp-grid-line, #ececec); }

/* design-time skeleton: client.js does not run on /design, so the template's rows are laid out
   statically (no absolute positioning / virtualization). */
.vcl-dbgrid-design .vcl-dbgrid-row  { position: static; height: 22px; line-height: 22px; }
.vcl-dbgrid-design .vcl-dbgrid-cell { line-height: 22px; }

/* v083: in-cell editor — an absolutely-positioned input over the cell being edited */
.vcl-dbgrid-editor {
  font: inherit;
  border: 1px solid var(--tp-accent, #0a64c0);
  outline: none;
  padding: 0 5px;
  background: var(--tp-input-bg, #ffffff);
  color: var(--tp-fg, #000000);
  z-index: 5;
}
/* v084: per-type editors — select (combo/lookup) trims side padding so the dropdown arrow fits; the
   checkbox editor centres its small box in the cell rather than left-aligning it. */
.vcl-dbgrid-editor-select { padding: 0 2px; }
.vcl-dbgrid-editor-check  { display: flex; align-items: center; justify-content: center; padding: 0; }

/* the read-only checkbox shown in a checkbox column's cell (display, not the editor). v1061: NOT the multi-select
   gutter's box -- this rule matched it too, so a click on it fell through to the cell and selected that row alone
   (dbpagergate: a two-row selection kept one) */
.vcl-dbgrid-cell > input[type="checkbox"]:not(.vcl-dbgrid-selbox) { margin: 0; vertical-align: middle; pointer-events: none; }

/* v085: row Insert/Delete footer toolbar (only built when the grid is editable) */
.vcl-dbgrid-foot {
  flex: 0 0 auto;
  display: flex;
  gap: 2px;
  padding: 1px 2px;
  background: var(--tp-surface-2, #f0f0f0);
  border-top: 1px solid var(--tp-border, #aaaaaa);
}
.vcl-dbgrid-foot-btn {
  width: 20px;
  height: 18px;
  line-height: 16px;
  padding: 0;
  font: bold 14px Tahoma, sans-serif;
  cursor: pointer;
  border: 1px solid var(--tp-input-border, #9aa0a6);
  border-radius: 2px;
  background: var(--tp-btn-bg, #ffffff);
  color: var(--tp-btn-fg, #333333);
}
.vcl-dbgrid-foot-btn:hover { background: var(--tp-hover, #e8f0fe); border-color: var(--tp-accent, #0a64c0); }
.vcl-dbgrid-foot-btn:active { background: var(--tp-accent-soft, #d6e4fb); }
/* v1061: the pager (paged = True) -- uniGUI's paging bar, at the footer's left; the row tools stay at its right */
.vcl-dbgrid-foot-btn:disabled { opacity: 0.4; cursor: default; background: var(--tp-btn-bg, #ffffff); border-color: var(--tp-input-border, #9aa0a6); }
.vcl-dbgrid-pager {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 2px;
  font: 12px Tahoma, "Segoe UI", sans-serif;
  color: var(--tp-fg, #222222);
}
.vcl-dbgrid-pg-word, .vcl-dbgrid-pg-of { padding: 0 2px; white-space: nowrap; }
.vcl-dbgrid-pg-no {
  width: 36px;
  height: 16px;
  padding: 0 2px;
  margin: 0 2px;
  box-sizing: border-box;
  text-align: center;
  font: 12px Tahoma, "Segoe UI", sans-serif;
  border: 1px solid var(--tp-input-border, #9aa0a6);
  border-radius: 2px;
  background: var(--tp-input-bg, #ffffff);
  color: var(--tp-fg, #222222);
}
.vcl-dbgrid-pg-refresh { margin-left: 4px; }
.vcl-dbgrid-pg-info {
  margin-left: auto;
  padding: 0 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--tp-muted, #555555);
}
/* the phantom new row being entered */
.vcl-dbgrid-row-new .vcl-dbgrid-cell:first-child { box-shadow: inset 2px 0 0 #1f9d4d; }

/* v089: per-column filter row (under the header, horizontally synced) */
.vcl-dbgrid-filter { flex: 0 0 auto; height: 22px; overflow: hidden; background: var(--tp-surface, #fafbfc); border-bottom: 1px solid var(--tp-grid-line, #cccccc); }
.vcl-dbgrid-filter-inner { position: relative; height: 100%; white-space: nowrap; will-change: transform; }
.vcl-dbgrid-filter-cell { display: inline-block; box-sizing: border-box; height: 22px; padding: 1px 2px; vertical-align: top; }
.vcl-dbgrid-filter-input {
  width: 100%; height: 18px; box-sizing: border-box;
  border: 1px solid var(--tp-input-border, #c4c9d0); border-radius: 2px;
  font: 12px Tahoma, sans-serif; padding: 0 3px; color: var(--tp-fg, #000000);
}
.vcl-dbgrid-filter-input:focus { outline: none; border-color: var(--tp-accent, #0a64c0); }

/* v090: collapsible group-header row */
.vcl-dbgrid-grouphdr {
  position: absolute; left: 0; box-sizing: border-box;
  background: var(--tp-tint, #dde6f2); border-bottom: 1px solid var(--tp-grid-line, #b8c4d6);
  font-weight: bold; color: var(--tp-fg, #1a3a5c); padding: 0 6px;
  cursor: pointer; overflow: hidden; white-space: nowrap;
}
.vcl-dbgrid-group-arrow { display: inline-block; width: 12px; color: var(--tp-accent, #0a64c0); }
.vcl-dbgrid-group-count { color: var(--tp-fg-muted, #5a6b80); font-weight: normal; }

/* v088: footer aggregates strip (horizontally synced like the header) */
.vcl-dbgrid-agg { flex: 0 0 auto; height: 20px; overflow: hidden; background: var(--tp-tint, #eef1f6); border-top: 1px solid var(--tp-border, #aaaaaa); }
.vcl-dbgrid-agg-inner { position: relative; height: 100%; white-space: nowrap; will-change: transform; }
.vcl-dbgrid-agg-cell {
  display: inline-block; box-sizing: border-box;
  height: 20px; line-height: 20px; padding: 0 6px;
  font-weight: bold; font-size: 12px; color: var(--tp-fg, #2a2a2a);
  overflow: hidden; white-space: nowrap; vertical-align: top;
}

/* TDBHTMLText */
/* v080: TDBHTMLText — a READ-ONLY block that renders the bound field's value as HTML (innerHTML), the data-aware
   sibling of TDBText (which renders the field as plain text). The field is expected to hold HTML markup (e.g. a
   stored article body); it's rendered, not escaped. On /design the HTML is server-rendered ({DataFieldHtml} — the
   raw field value); in the running app the live feed sets the body's innerHTML. Scrolls when the content overflows.
   Inserted <script> never executes (browsers don't run scripts set via innerHTML). Geometry/surface come from the
   {Style} chain. */
.vcl-dbhtmltext {
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: auto;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbhtmltext-body {
  padding: 3px 6px;
  font: inherit;     /* inherit the wrapper's font (model fontFamily/fontSize/bold/...) */
  color: inherit;    /* and the model text colour */
}
/* keep common block elements from blowing out the box margins */
.vcl-dbhtmltext-body > :first-child { margin-top: 0; }
.vcl-dbhtmltext-body > :last-child { margin-bottom: 0; }
.vcl-dbhtmltext-body img { max-width: 100%; }

/* TDBImage */
/* v059: TDBImage — a data-aware image. The bound dataField holds an image URL (or a data: URI); the SSR sets
   <img src> to the FIRST row's value ({DataFieldImageSrc}), and the live feed (TMySQLQuery/client.js) re-points it
   as the cursor moves. When there's no value the <img> is hidden (empty src) and the dashed picture placeholder
   below shows through — so a freshly-bound TDBImage reads as "an image goes here" on /design. */
.vcl-dbimage {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  border: 1px solid #c4c9d0;
  background: #f4f5f7
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48'%3E%3Crect x='4' y='8' width='40' height='32' rx='3' fill='none' stroke='%23a0a6b0' stroke-width='2'/%3E%3Ccircle cx='16' cy='19' r='4' fill='%23a0a6b0'/%3E%3Cpath d='M10 39 L21 25 L29 33 L35 27 L42 35 L42 39 Z' fill='%23c4c9d0'/%3E%3C/svg%3E")
    center / 40px no-repeat;
}
.vcl-dbimage-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;   /* overridden inline by the {{fit}} token */
}
/* no bound value yet -> hide the <img> so the placeholder behind shows */
.vcl-dbimage-img[src=""],
.vcl-dbimage-img:not([src]) { opacity: 0; }

/* TDBListBox */
/* v053: TDBListBox — a data-aware list of the bound field across ALL rows of its dataset (the web idiom: show
   the data, not a fixed Items list). The current/selected row is highlighted; clicking an item navigates the
   shared cursor (see TDBListBox/client.js) so the navigator + other bound controls follow. On /design the list is
   server-rendered ({DataFieldList}); in the running app the live feed re-renders it. */
.vcl-dblistbox {
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;
}
.vcl-dblistbox-items {
  height: 100%;
  overflow-y: auto;
}
.vcl-dblistbox-item {
  padding: 1px 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  color: inherit;     /* inherit the model text colour (font-family/size are inherited by the divs automatically) */
}
.vcl-dblistbox-item:hover { background: #eaf2fb; }
.vcl-dblistbox-item-sel,
.vcl-dblistbox-item-sel:hover { background: #0a64c0; color: #fff; }
/* empty / unbound (no rows): a faint hint so the box doesn't read as broken */
.vcl-dblistbox-items:empty::before {
  content: "(no data)";
  display: block;
  padding: 2px 5px;
  color: #aaa;
  font-style: italic;
}

/* TDBLookupComboBox */
/* v077: TDBLookupComboBox — a data-aware lookup dropdown. Like TDBComboBox, but its <option>s come from a SECOND
   (lookup) dataset (listSource): each option's value is the lookup's keyField and its text is the listField. The
   option whose value matches the MAIN dataset's bound field (dataField) is selected. Display-only in browse mode
   (pointer-events:none); in edit mode the shared feed flips pointer-events on so the dropdown is usable, and the
   chosen key is written to the main field on Post (see TMySQLQuery/client.js boundEditControls + dbPost). On
   /design the options are server-rendered ({DataLookupOptions}); in the running app the live feed re-renders them. */
.vcl-dblookupcombobox {
  box-sizing: border-box;
  display: inline-flex;
  align-items: stretch;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dblookupcombobox-select {
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: inherit;
  color: inherit;
  padding: 1px 3px;
  pointer-events: none;   /* display-only in browse mode; edit mode flips this to 'auto' via setEditable */
}

/* TDBLookupListBox */
/* v076: TDBLookupListBox — a data-aware lookup list. Like TDBListBox, but the rows come from a SECOND (lookup)
   dataset (listSource): each item shows the lookup's listField and carries its keyField as data-key. The item whose
   key matches the MAIN dataset's bound field (dataField) is highlighted. Display-only in browse mode (pointer-events
   are off); in edit mode the shared feed flips pointer-events on so a click moves the selection, and the chosen key
   is written to the main field on Post (see TMySQLQuery/client.js boundEditControls + dbPost). On /design the list
   is server-rendered ({DataLookupList}); in the running app the live feed re-renders it. */
.vcl-dblookuplistbox {
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;
}
.vcl-dblookuplistbox-items {
  height: 100%;
  overflow-y: auto;
  pointer-events: none;   /* display-only in browse mode; edit mode flips this to 'auto' via setEditable */
}
.vcl-dblookuplistbox-item {
  padding: 1px 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  color: inherit;     /* inherit the model text colour (font-family/size inherited by the divs automatically) */
}
.vcl-dblookuplistbox-item:hover { background: #eaf2fb; }
.vcl-dblookuplistbox-item-sel,
.vcl-dblookuplistbox-item-sel:hover { background: #0a64c0; color: #fff; }
/* empty / unbound (no lookup rows): a faint hint so the box doesn't read as broken */
.vcl-dblookuplistbox-items:empty::before {
  content: "(no data)";
  display: block;
  padding: 2px 5px;
  color: #aaa;
  font-style: italic;
}

/* TDBMemo */
/* v078: TDBMemo — a data-aware MULTI-LINE edit (the <textarea> sibling of TDBEdit). On /design its content is the
   LIVE bound field value (the {DataFieldValue} token, resolved through designDbUnit), or a [fieldName] placeholder
   when the query isn't open. Display-only in browse mode (readonly attr); the shared feed unlocks it in edit mode
   and writes the value back to the field on Post (see TMySQLQuery/client.js refreshDbControls + boundEditControls).
   Geometry/surface come from the {Style} chain. */
.vcl-dbmemo {
  box-sizing: border-box;
  display: flex;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the inline {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbmemo-input {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  border: none;
  outline: none;
  resize: none;
  background: transparent;
  font: inherit;     /* inherit the wrapper's font (model fontFamily/fontSize/bold/...) */
  color: inherit;    /* and the model text colour */
  padding: 2px 4px;
  white-space: pre-wrap;   /* wordWrap=True (default): wrap long lines */
}
/* wordWrap=False -> don't wrap; scroll horizontally instead */
.vcl-dbmemo-input[data-wordwrap="False"] { white-space: pre; overflow-x: auto; }

/* TDBNavigator */
/* v052: TDBNavigator — a row of dataset navigation buttons (First / Prior / Next / Last / Refresh). Each
   button drives the bound dataset through window.__tpDb (see TDBNavigator/client.js), so clicking moves the
   shared cursor and every data-aware control bound to the same query updates live. Classic VCL navigator look:
   small raised gray buttons in a row. */
.vcl-dbnav {
  box-sizing: border-box;
  display: inline-flex;
  align-items: stretch;
  gap: 0;
  background: #f0f0f0;
  border: 1px solid #a0a0a0;
  border-radius: 3px;
  overflow: hidden;
  font: 13px "Segoe UI Symbol", Tahoma, sans-serif;
  user-select: none;
}
/* IDE v1065: the divider is each button's LEFT edge, pulled 1px under its neighbour -- the first visible
   one's lies under the frame (clipped), whichever buttons VisibleButtons hides */
.vcl-dbnav-btn {
  flex: 1 1 0;
  min-width: 0;
  border: none;
  border-left: 1px solid #c8c8c8;
  margin-left: -1px;
  background: linear-gradient(#fdfdfd, #e6e6e6);
  color: #222;
  cursor: pointer;
  padding: 0 6px;
  line-height: 1;
  font: inherit;
}
.vcl-dbnav-btn:hover  { background: linear-gradient(#ffffff, #eaf2fb); }
.vcl-dbnav-btn:active { background: linear-gradient(#dcdcdc, #cfcfcf); }
/* IDE v1065: greyed as Delphi's -- nothing to move to, nothing being edited, no cached updates */
.vcl-dbnav-btn:disabled,
.vcl-dbnav-btn:disabled:hover,
.vcl-dbnav-btn:disabled:active { background: linear-gradient(#fdfdfd, #e6e6e6); color: #a0a0a0; cursor: default; }
/* IDE v1065: VisibleButtons, the set as the .dfm writes it ("[nbFirst, nbPrior]") -- a button shows when its
   name is followed by ',' or ']' (so nbCancel is not read out of nbCancelUpdates) */
.vcl-dbnav[data-vb]:not([data-vb*="nbFirst," i]):not([data-vb*="nbFirst]" i]) > [data-nav="first"],
.vcl-dbnav[data-vb]:not([data-vb*="nbPrior," i]):not([data-vb*="nbPrior]" i]) > [data-nav="prior"],
.vcl-dbnav[data-vb]:not([data-vb*="nbNext," i]):not([data-vb*="nbNext]" i]) > [data-nav="next"],
.vcl-dbnav[data-vb]:not([data-vb*="nbLast," i]):not([data-vb*="nbLast]" i]) > [data-nav="last"],
.vcl-dbnav[data-vb]:not([data-vb*="nbInsert," i]):not([data-vb*="nbInsert]" i]) > [data-nav="insert"],
.vcl-dbnav[data-vb]:not([data-vb*="nbDelete," i]):not([data-vb*="nbDelete]" i]) > [data-nav="delete"],
.vcl-dbnav[data-vb]:not([data-vb*="nbEdit," i]):not([data-vb*="nbEdit]" i]) > [data-nav="edit"],
.vcl-dbnav[data-vb]:not([data-vb*="nbPost," i]):not([data-vb*="nbPost]" i]) > [data-nav="post"],
.vcl-dbnav[data-vb]:not([data-vb*="nbCancel," i]):not([data-vb*="nbCancel]" i]) > [data-nav="cancel"],
.vcl-dbnav[data-vb]:not([data-vb*="nbRefresh," i]):not([data-vb*="nbRefresh]" i]) > [data-nav="refresh"],
.vcl-dbnav[data-vb]:not([data-vb*="nbApplyUpdates," i]):not([data-vb*="nbApplyUpdates]" i]) > [data-nav="applyupdates"],
.vcl-dbnav[data-vb]:not([data-vb*="nbCancelUpdates," i]):not([data-vb*="nbCancelUpdates]" i]) > [data-nav="cancelupdates"] { display: none; }

/* TDBQRCode */
/* TDBQRCode — data-aware QR code. Host carries the geometry (inline {Style}); client.js renders a
   crisp SVG of the bound field's value, refreshed as the dataset cursor moves. On /design (no
   client.js) the static template SVG skeleton is shown. */

.vcl-dbqrcode {
  box-sizing: border-box;
  display: block;
  overflow: hidden;
  background: transparent;
  line-height: 0;
}
.vcl-dbqrcode > svg { display: block; width: 100%; height: 100%; }

/* TDBRadioGroup */
/* v055: TDBRadioGroup — radio options from its `items` (one per line); the option equal to the bound field value
   is selected (the {DataFieldRadios} token; the live feed re-checks the match). Display-only (pointer-events:none)
   — the runtime write-back (Post) is a later step. */
.vcl-dbradiogroup {
  box-sizing: border-box;
  border: 1px solid #a0a0a0;
  border-radius: 3px;
  margin: 0;
  padding: 2px 8px 6px;
  background: #f0f0f0;
  font: 13px Tahoma, "Segoe UI", sans-serif;
  color: #000;
  overflow: auto;
}
.vcl-dbradiogroup-cap {
  padding: 0 4px;
  font-weight: 600;
}
.vcl-dbradiogroup-items {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-top: 2px;
}
.vcl-dbradiogroup-item {
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  cursor: default;
}
.vcl-dbradiogroup-item input {
  pointer-events: none;   /* display-only */
  margin: 0;
  flex: 0 0 auto;
}

/* TDBRichEdit */
/* v081: TDBRichEdit — a rich-text EDITOR bound to a field. The body is a contenteditable div rendering the field's
   HTML; the user edits it (native rich text: Ctrl+B/I/U, paste-with-formatting) and on BLUR the edited innerHTML is
   written back to the field + Posted (autoEdit-style — like the v079 date picker; no edit/post buttons needed). A
   readOnly TDBRichEdit is not editable (contenteditable=false). The editable sibling of TDBHTMLText (read-only).
   On /design the field HTML is server-rendered ({DataFieldHtml}); the running app re-fills the body live (but never
   while it's focused, so it can't clobber a mid-edit). Geometry/surface come from the {Style} chain. */
.vcl-dbrichedit {
  box-sizing: border-box;
  background: #fff;
  border: 1px solid #7a7a7a;
  overflow: auto;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbrichedit-body {
  min-height: 100%;
  padding: 3px 6px;
  outline: none;
  cursor: text;
  font: inherit;     /* inherit the wrapper's font (model fontFamily/fontSize/bold/...) */
  color: inherit;    /* and the model text colour */
}
.vcl-dbrichedit-body:focus { box-shadow: inset 0 0 0 2px #0a64c0; }
.vcl-dbrichedit-body > :first-child { margin-top: 0; }
.vcl-dbrichedit-body > :last-child { margin-bottom: 0; }
.vcl-dbrichedit-body img { max-width: 100%; }

/* TDBText */
/* v055: TDBText — a read-only label showing the bound field's current value (the {DataFieldValue} token; the
   live feed re-renders its text). On /design it shows the value or a [fieldName] placeholder. */
.vcl-dbtext {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  overflow: hidden;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* FALLBACK — the {Style} FontCss overrides when the model sets font */
  color: #000;
}
.vcl-dbtext-val {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vcl-dbtext-val:empty::before { content: ""; }

/* TEmbAnalogIn */
/* v1010: TEmbAnalogIn -- design-time puck for an analog input read as a measurement.
   A small scope screen with a slope on it: a voltage, not a knob. */
.vcl-embain { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embain-wave { position:relative; width:24px; height:16px; border-radius:2px; overflow:hidden;
  border:2px solid var(--tp-emb-off,#7f8c8d); background:var(--tp-emb-dial,#e4e9ee); }
/* the trace: a line rising left to right */
.vcl-embain-trace { position:absolute; left:1px; bottom:3px; width:24px; height:2px;
  background:var(--tp-emb-off,#7f8c8d); transform-origin:0 50%; transform:rotate(-24deg); }
.vcl-embain-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbAnalogOut */
/* v1010: TEmbAnalogOut -- design-time puck for a DAC output: a screen with a staircase on it,
   a voltage the program sets. */
.vcl-embaout { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embaout-wave { position:relative; width:24px; height:16px; border-radius:2px; overflow:hidden;
  border:2px solid var(--tp-emb-off,#7f8c8d); background:var(--tp-emb-dial,#e4e9ee); }
/* the staircase: two steps drawn by the borders of one box */
.vcl-embaout-step { position:absolute; left:2px; top:3px; width:8px; height:7px;
  border-right:2px solid var(--tp-emb-off,#7f8c8d); border-top:2px solid var(--tp-emb-off,#7f8c8d); }
.vcl-embaout-step::after { content:""; position:absolute; left:10px; top:-5px; width:8px; height:0;
  border-top:2px solid var(--tp-emb-off,#7f8c8d); }
.vcl-embaout-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbAnalogSensor */
.vcl-embana { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embana-model { min-width:36px; padding:2px 3px; border:1px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:var(--tp-emb-dial,#e4e9ee); text-align:center; font-weight:bold; }
.vcl-embana-model::after { content:"V"; }
.vcl-embana-amDivider::after { content:"0-10 V"; }
.vcl-embana-amCurrent::after { content:"4-20 mA"; }
.vcl-embana-amNtc::after { content:"NTC \00B0 C"; }
.vcl-embana-amPercent::after { content:"%"; }
.vcl-embana-amLinear::after { content:"y = ax+b"; }
.vcl-embana-amTable::after { content:"table"; }
.vcl-embana-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbBle */
/* v1004: TEmbBle -- a design-time puck: the Bluetooth rune, the radio's name under it. */
.vcl-embble { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embble-rune { font:bold 18px Tahoma,sans-serif; color:#1e6fd9; line-height:18px; }
.vcl-embble-name { letter-spacing:.3px; opacity:.75; }

/* TEmbBleChar */
/* v1024: TEmbBleChar -- a design-time puck like TEmbBle's: the Bluetooth rune, the characteristic's UUID. */
.vcl-embblechar { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px dashed var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embblechar-rune { font:bold 14px Tahoma,sans-serif; color:#1e6fd9; line-height:14px; }
.vcl-embblechar-name { letter-spacing:.3px; opacity:.75; }

/* TEmbBME280 */
/* v1019: TEmbBME280 -- an I2C temperature + pressure + humidity sensor. The puck reads "--.-" until a
   board is running; the live monitor fills in the real values over USB. */
.vcl-embbme280 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:1px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embbme280-val { font:bold 12px Consolas,monospace; }
.vcl-embbme280-hum, .vcl-embbme280-prs { opacity:.75; letter-spacing:.3px; }

/* TEmbButton */
/* v490-embedded-components: TEmbButton -- the design-time puck for a debounced input pin. */
.vcl-embbutton {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: var(--tp-emb-bg, #f4f6f8);
  border: 1px solid var(--tp-emb-border, #b9c2cc);
  border-radius: 6px;
  font: 9px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);
  user-select: none;
}
.vcl-embbutton-cap {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fdfdfd 0%, #d7dde3 100%);
  border: 1px solid #9aa5b1;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.18);
}
.vcl-embbutton-pin {
  letter-spacing: .3px;
  opacity: .75;
}

/* TEmbBuzzer */
.vcl-embbuzzer { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embbuzzer-can { width:18px; height:18px; border-radius:50%; background:var(--tp-emb-off,#7f8c8d);
  box-shadow:inset 0 0 0 6px var(--tp-emb-off,#7f8c8d), inset 0 0 0 9px #1f2937; }
.vcl-embbuzzer-pin { opacity:.75; letter-spacing:.2px; }

/* TEmbDHT20 */
/* v501: TEmbDHT20 -- an I2C temperature + humidity sensor. The puck reads "--.-" because there is
   nothing to show until a board is running; the live monitor fills in the real values over USB. */
.vcl-embdht20 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embdht20-val { font:bold 12px Consolas,monospace; }
.vcl-embdht20-hum { opacity:.75; letter-spacing:.3px; }

/* TEmbDigitalSensor */
.vcl-embdig { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embdig-model { min-width:36px; padding:2px 3px; border:1px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:var(--tp-emb-dial,#e4e9ee); text-align:center; font-weight:bold; }
.vcl-embdig-model::after { content:"on/off"; }
.vcl-embdig-dmCounter::after { content:"count"; }
.vcl-embdig-dmFrequency::after { content:"Hz"; }
.vcl-embdig-dmFlow::after { content:"l/min"; }
.vcl-embdig-dmTacho::after { content:"rpm"; }
.vcl-embdig-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbDS18B20 */
/* v500: TEmbDS18B20 -- a 1-Wire temperature sensor. The puck reads "--.-" because there is nothing
   to show until a board is running; the live monitor fills in the real value over USB. */
.vcl-embds18b20 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embds18b20-val { font:bold 12px Consolas,monospace; }
.vcl-embds18b20-pin { opacity:.75; letter-spacing:.3px; }
/* TEmbEncoder */
/* IDE v1109: TEmbEncoder -- the design-time puck for a rotary encoder (A / B, a push switch). */
.vcl-embencoder {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: var(--tp-emb-bg, #f4f6f8);
  border: 1px solid var(--tp-emb-border, #b9c2cc);
  border-radius: 6px;
  font: 9px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);
  user-select: none;
}
.vcl-embencoder-knob {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: repeating-conic-gradient(#7d8792 0 8deg, #c9d0d7 8deg 22.5deg);
  border: 1px solid #6b7580;
  box-shadow: inset 0 0 0 5px #e9edf1;
}
.vcl-embencoder-pin {
  letter-spacing: .3px;
  opacity: .75;
}

/* TEmbEthernet */
/* v1094: TEmbEthernet -- the ESP32-P4's own Ethernet, a design-time puck for a NON-VISUAL component (TEmbW5500's look). */
.vcl-embeth { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embeth-name { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embeth-ip { opacity:.75; letter-spacing:.2px; }

/* TEmbGP8403 */
/* v500: TEmbGP8403 -- one channel of a 2-channel I2C DAC. The puck shows the commanded percentage,
   because that is the number the firmware actually sends; volts depend on the range setting. */
.vcl-embgp8403 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embgp8403-val { font:bold 13px Consolas,monospace; }
.vcl-embgp8403-id { opacity:.75; letter-spacing:.2px; }
/* TEmbI2C */
/* v499: TEmbI2C -- a design-time puck for a BUS, not a device. Devices reference it by name, and the
   generator initialises it before them regardless of where either was dropped on the form. */
.vcl-embi2c { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embi2c-bus { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embi2c-pins { opacity:.75; letter-spacing:.2px; }
/* TEmbI2CSensor */
.vcl-embi2cs { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embi2cs-model { min-width:40px; padding:2px 3px; border:1px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:var(--tp-emb-dial,#e4e9ee); text-align:center; font-weight:bold; }
.vcl-embi2cs-model::after { content:"BME280"; }
.vcl-embi2cs-imBmp280::after { content:"BMP280"; }
.vcl-embi2cs-imAht20::after { content:"AHT20"; }
.vcl-embi2cs-imSht3x::after { content:"SHT3x"; }
.vcl-embi2cs-imSht4x::after { content:"SHT4x"; }
.vcl-embi2cs-imIna219::after { content:"INA219"; }
.vcl-embi2cs-imAds1115::after { content:"ADS1115"; }
.vcl-embi2cs-imBh1750::after { content:"BH1750"; }
.vcl-embi2cs-imMcp9808::after { content:"MCP9808"; }
.vcl-embi2cs-imTmp117::after { content:"TMP117"; }
.vcl-embi2cs-addr { letter-spacing:.3px; opacity:.75; }

/* TEmbLcd */
/* v1020: TEmbLcd -- an HD44780 character LCD. The glass shows the Caption until a board runs; the live
   monitor then shows the rows the glass shows. */
.vcl-emblcd { box-sizing:border-box; display:flex; flex-direction:column; align-items:flex-start;
  justify-content:center; gap:1px; padding:3px 6px; background:#9bc53d; border:3px solid #2f3a1f;
  border-radius:3px; font:bold 11px Consolas,monospace; color:#1f2d0a; user-select:none; overflow:hidden; }
.vcl-emblcd-glass { white-space:pre; }
.vcl-emblcd-size { opacity:.6; font-size:9px; }

/* TEmbLed */
/* v490-embedded-components: TEmbLed -- a design-time puck, not a web control.
   It exists so the designer canvas shows something recognisable for a pin you cannot see. The lamp
   reflects the State property so a form with several LEDs reads at a glance. */
.vcl-embled {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: var(--tp-emb-bg, #f4f6f8);
  border: 1px solid var(--tp-emb-border, #b9c2cc);
  border-radius: 6px;
  font: 9px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);
  user-select: none;
}
.vcl-embled-lamp {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--tp-emb-off, #7f8c8d);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
}
/* State=True lights it, so the canvas shows the boot state the firmware will actually drive. */
.vcl-embled[data-state="True"] .vcl-embled-lamp,
.vcl-embled[data-state="true"] .vcl-embled-lamp {
  background: var(--tp-emb-on, #2ecc40);
  box-shadow: 0 0 6px 1px rgba(46, 204, 64, 0.75);
}
.vcl-embled-pin {
  letter-spacing: .3px;
  opacity: .75;
}

/* TEmbModbus */
/* v1029: TEmbModbus -- a Modbus RTU master on a UART (the ESP32-C6). Not visual on the board; the tile
   names the protocol and the two pins, the live monitor shows the registers over USB. */
.vcl-embmodbus { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embmodbus-name { font:bold 11px Consolas,monospace; }
.vcl-embmodbus-pin { opacity:.75; letter-spacing:.3px; }

/* TEmbModbusSensor */
.vcl-embmbs { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embmbs-model { min-width:40px; padding:2px 2px; border:1px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:var(--tp-emb-dial,#e4e9ee); text-align:center; font-weight:bold; font-size:8px; }
.vcl-embmbs-model::after { content:"MODBUS"; }
.vcl-embmbs-mmPta9b01::after { content:"PTA9B01"; }
.vcl-embmbs-mmXyMd02::after { content:"XY-MD02"; }
.vcl-embmbs-mmPzem004t::after { content:"PZEM-004T"; }
.vcl-embmbs-mmSdm120::after { content:"SDM120"; }
.vcl-embmbs-addr { letter-spacing:.3px; opacity:.75; }

/* TEmbMosfet */
/* v504: TEmbMosfet -- design-time puck for a MOSFET power switch (DFRobot DFR0457 and friends).
   Same electrical story as TEmbRelay -- one GPIO, State means "the LOAD is on" -- so the puck shows
   the LOAD, not the pin level. Drawn as a MOSFET body with its channel bar rather than a relay coil,
   because on a form carrying both, "which one is the silent solid-state one" is the thing you need
   to read at a glance. */
.vcl-embmosfet { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
/* the body: a squat package with the drain/source channel down its right edge */
.vcl-embmosfet-body { position:relative; width:20px; height:14px; border:2px solid var(--tp-emb-off,#7f8c8d);
  border-radius:2px; background:transparent; }
/* the gate bar, offset off the body's left edge the way a MOSFET is drawn */
.vcl-embmosfet-gate { position:absolute; left:-5px; top:2px; width:3px; height:6px;
  background:var(--tp-emb-off,#7f8c8d); }
.vcl-embmosfet[data-state="True"] .vcl-embmosfet-body,
.vcl-embmosfet[data-state="true"] .vcl-embmosfet-body { border-color:var(--tp-emb-on,#2ecc40);
  background:rgba(46,204,64,.25); box-shadow:0 0 5px rgba(46,204,64,.7); }
.vcl-embmosfet[data-state="True"] .vcl-embmosfet-gate,
.vcl-embmosfet[data-state="true"] .vcl-embmosfet-gate { background:var(--tp-emb-on,#2ecc40); }
.vcl-embmosfet-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbMqtt */
.vcl-embmqtt { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; overflow:hidden; }
.vcl-embmqtt-name { padding:2px 4px; border:1px solid var(--tp-emb-off,#7f8c8d); border-radius:3px;
  background:var(--tp-emb-dial,#e4e9ee); font-weight:bold; letter-spacing:.5px; }
.vcl-embmqtt-host { max-width:52px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; opacity:.75; }

/* TEmbNeoPixel */
.vcl-embneopixel { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:4px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embneopixel-strip { display:flex; gap:2px; padding:2px; border-radius:2px; background:#1f2937; }
.vcl-embneopixel-strip i { display:block; width:6px; height:6px; border-radius:1px; background:#ef4444; }
.vcl-embneopixel-strip i:nth-child(2) { background:#22c55e; }
.vcl-embneopixel-strip i:nth-child(3) { background:#3b82f6; }
.vcl-embneopixel-strip i:nth-child(4) { background:#f59e0b; }
.vcl-embneopixel-pin { opacity:.75; letter-spacing:.2px; }

/* TEmbPotmeter */
/* v504: TEmbPotmeter -- design-time puck for an analog knob on GP26/27/28.
   Drawn as a rotary dial with a pointer at roughly 10 o'clock: a knob is the one embedded part
   whose real-world shape everyone already recognises, so the puck does not need a label to read. */
.vcl-embpot { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embpot-dial { position:relative; width:18px; height:18px; border-radius:50%;
  border:2px solid var(--tp-emb-off,#7f8c8d); background:var(--tp-emb-dial,#e4e9ee); }
/* the pointer: a stub from the centre out to the rim */
.vcl-embpot-knob { position:absolute; left:50%; top:50%; width:2px; height:7px;
  background:var(--tp-emb-off,#7f8c8d); transform-origin:1px 0;
  transform:translate(-1px,-1px) rotate(-135deg); }
.vcl-embpot-pin { letter-spacing:.3px; opacity:.75; }

/* TEmbPwm */
/* v493-embedded-pwm: TEmbPwm -- a design-time puck, not a web control.
   The bar shows the boot duty the firmware will actually load, so a form full of PWM outputs reads
   at a glance and a Level left at 0 is visibly a Level left at 0. */
.vcl-embpwm {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: var(--tp-emb-bg, #f4f6f8);
  border: 1px solid var(--tp-emb-border, #b9c2cc);
  border-radius: 6px;
  font: 9px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);
  user-select: none;
}
.vcl-embpwm-bar {
  display: block;
  width: 30px;
  height: 8px;
  border-radius: 4px;
  background: var(--tp-emb-off, #7f8c8d);
  overflow: hidden;
}
.vcl-embpwm-fill {
  display: block;
  height: 100%;
  background: var(--tp-emb-on, #2ecc40);
  /* width comes from the Level property, inline -- 0% is a legitimate, meaningful state */
}
.vcl-embpwm-pin {
  letter-spacing: .3px;
  opacity: .75;
}

/* TEmbReadVar */
/* v497: TEmbReadVar -- a value the firmware pushes to the IDE's live monitor. The puck shows "--"
   at design time because there is nothing to show until a board is running and publishing. */
.vcl-embreadvar { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embreadvar-val { font:bold 13px Consolas,monospace; letter-spacing:.5px; }
.vcl-embreadvar-lbl { opacity:.75; letter-spacing:.3px; }
/* TEmbRelay */
/* v496: TEmbRelay -- a design-time puck. The coil lights on State, and State means "the load is ON",
   not "the pin is high": rmNC wiring inverts the pin, which is why the puck shows the LOAD. */
.vcl-embrelay { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embrelay-coil { width:20px; height:14px; border:2px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:repeating-linear-gradient(90deg,var(--tp-emb-off,#7f8c8d) 0 2px,transparent 2px 5px); }
.vcl-embrelay[data-state="True"] .vcl-embrelay-coil,
.vcl-embrelay[data-state="true"] .vcl-embrelay-coil { border-color:var(--tp-emb-on,#2ecc40);
  background:repeating-linear-gradient(90deg,var(--tp-emb-on,#2ecc40) 0 2px,transparent 2px 5px);
  box-shadow:0 0 5px rgba(46,204,64,.7); }
.vcl-embrelay-pin { letter-spacing:.3px; opacity:.75; }
/* TEmbRTC */
.vcl-embrtc { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embrtc-face { position:relative; width:18px; height:18px; border-radius:50%;
  border:2px solid var(--tp-emb-off,#7f8c8d); box-sizing:border-box; }
.vcl-embrtc-face::before { content:''; position:absolute; left:6px; top:2px; width:2px; height:6px; background:#244a7c; }
.vcl-embrtc-face::after { content:''; position:absolute; left:7px; top:7px; width:5px; height:2px; background:#f59e0b; }
.vcl-embrtc-model { font-weight:bold; }
.vcl-embrtc-model::after { content:"DS3231"; }
.vcl-embrtc-rmDs1307::after { content:"DS1307"; }
.vcl-embrtc-rmPcf8563::after { content:"PCF8563"; }

/* TEmbSdCard */
/* v983: TEmbSdCard -- a design-time puck for a NON-VISUAL component. It shows what the board cannot:
   which pins it took, so two devices fighting over one bus are visible on the form. */
.vcl-embsdcard { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embsdcard-name { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embsdcard-pins, .vcl-embsdcard-ip { opacity:.75; letter-spacing:.2px; }

/* TEmbServo */
.vcl-embservo { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embservo-dial { position:relative; width:26px; height:14px; overflow:hidden; }
.vcl-embservo-dial::before { content:''; position:absolute; left:0; top:0; width:24px; height:24px;
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:50%; }
.vcl-embservo-arm { position:absolute; left:3px; top:12px; width:10px; height:2px; background:#f59e0b;
  transform-origin:10px 1px; }
.vcl-embservo-pin { opacity:.75; letter-spacing:.2px; }

/* TEmbSPI */
/* v983: TEmbSPI -- a design-time puck for a NON-VISUAL component. It shows what the board cannot:
   which pins it took, so two devices fighting over one bus are visible on the form. */
.vcl-embspi { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embspi-bus { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embspi-pins, .vcl-embspi-ip { opacity:.75; letter-spacing:.2px; }

/* TEmbSPIDevice */
/* v985: TEmbSPIDevice -- a device hanging off a TEmbSPI. The puck shows the ONE thing that is its
   own, the select line, because everything else it uses belongs to the bus it names. */
.vcl-embspidev { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embspidev-dev { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embspidev-pins { opacity:.75; letter-spacing:.2px; }

/* TEmbSPISensor */
.vcl-embspis { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embspis-model { min-width:40px; padding:2px 2px; border:1px solid var(--tp-emb-off,#7f8c8d);
  border-radius:3px; background:var(--tp-emb-dial,#e4e9ee); text-align:center; font-weight:bold; font-size:8px; }
.vcl-embspis-model::after { content:"MAX31865"; }
.vcl-embspis-smMax31855::after { content:"MAX31855"; }
.vcl-embspis-smMax6675::after { content:"MAX6675"; }
.vcl-embspis-smMcp3008::after { content:"MCP3008"; }
.vcl-embspis-cs { letter-spacing:.3px; opacity:.75; }

/* TEmbSqlite */
/* v1054: TEmbSqlite -- a design-time puck for a non-visual component: the database file it keeps on the card. */
.vcl-embsqlite { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; overflow:hidden; }
.vcl-embsqlite-rune { font:bold 12px Consolas,monospace; color:#28466e; line-height:14px; }
.vcl-embsqlite-file { opacity:.75; letter-spacing:.2px; max-width:52px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* TEmbSSD1309 */
/* v500: TEmbSSD1309 -- design-time "glass" for a 128x64 OLED. It is a placeholder, not a preview:
   the panel is WRITE-ONLY over I2C, so what it actually shows can only be known from the firmware's
   own text journal, which the live monitor reads back over USB. */
.vcl-embssd1309 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px;
  background:linear-gradient(160deg,#11161c 0%,#1b2530 60%,#0d1218 100%);
  border:1px solid var(--tp-emb-border,#4a5560); border-radius:4px;
  font:9px Tahoma,sans-serif; color:#ffe9cf; user-select:none; }
.vcl-embssd1309-glass { font:bold 12px Consolas,monospace; letter-spacing:1px; opacity:.9; }
.vcl-embssd1309-id { opacity:.6; letter-spacing:.3px; }
/* TEmbSsh */
/* v1050: TEmbSsh -- a design-time puck for a non-visual component: a prompt over the protocol's name. */
.vcl-embssh { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embssh-rune { font:bold 15px Consolas,monospace; color:#14783c; line-height:16px; }
.vcl-embssh-name { letter-spacing:.3px; opacity:.75; }

/* TEmbSwitch */
/* v496: TEmbSwitch -- a design-time puck. A physical toggle is latched BY NATURE, so this reads a
   position, not a press; the knob shows the boot State the firmware will assume. */
.vcl-embswitch { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:3px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embswitch-body { position:relative; width:28px; height:14px; border-radius:7px;
  background:var(--tp-emb-off,#7f8c8d); transition:background .12s; }
.vcl-embswitch-knob { position:absolute; top:2px; left:2px; width:10px; height:10px;
  border-radius:50%; background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.4); }
.vcl-embswitch[data-state="True"] .vcl-embswitch-body,
.vcl-embswitch[data-state="true"] .vcl-embswitch-body { background:var(--tp-emb-on,#2ecc40); }
.vcl-embswitch[data-state="True"] .vcl-embswitch-knob,
.vcl-embswitch[data-state="true"] .vcl-embswitch-knob { left:16px; }
.vcl-embswitch-pin { letter-spacing:.3px; opacity:.75; }
/* TEmbTimer */
/* v492-embedded-clock-timer: TEmbTimer -- a design-time puck, not a web control.
   Same reasoning as TEmbLed: the thing it represents is invisible, so the canvas has to say what it
   is. The dial dims when Enabled is False, because a timer that will not start on boot is exactly
   the sort of thing you want to notice on the form rather than in the firmware. */
.vcl-embtimer {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: var(--tp-emb-bg, #f4f6f8);
  border: 1px solid var(--tp-emb-border, #b9c2cc);
  border-radius: 6px;
  font: 9px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);
  user-select: none;
}
.vcl-embtimer-dial {
  position: relative;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--tp-emb-on, #2ecc40);
  background: transparent;
}
/* The hand. Two borders on a zero-size box beat an SVG here: the puck has to render inside the
   designer's own stylesheet with no extra assets to load. */
.vcl-embtimer-dial::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 2px;
  width: 1px;
  height: 6px;
  background: var(--tp-emb-on, #2ecc40);
  transform-origin: bottom center;
  transform: rotate(45deg);
}
.vcl-embtimer[data-enabled="False"] .vcl-embtimer-dial,
.vcl-embtimer[data-enabled="false"] .vcl-embtimer-dial {
  border-color: var(--tp-emb-off, #7f8c8d);
  opacity: .55;
}
.vcl-embtimer[data-enabled="False"] .vcl-embtimer-dial::after,
.vcl-embtimer[data-enabled="false"] .vcl-embtimer-dial::after {
  background: var(--tp-emb-off, #7f8c8d);
}
.vcl-embtimer-int {
  letter-spacing: .3px;
  opacity: .75;
}

/* TEmbUART */
.vcl-embuart { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embuart-bus { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embuart-pins { opacity:.75; letter-spacing:.2px; }

/* TEmbW5500 */
/* v983: TEmbW5500 -- a design-time puck for a NON-VISUAL component. It shows what the board cannot:
   which pins it took, so two devices fighting over one bus are visible on the form. */
.vcl-embw5500 { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:8px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embw5500-name { font:bold 11px Consolas,monospace; letter-spacing:.5px; }
.vcl-embw5500-pins, .vcl-embw5500-ip { opacity:.75; letter-spacing:.2px; }

/* TEmbWebButton */
/* v500: TEmbWebButton -- a REMOTE press. It has no hardware at all: the IDE (or any host app) sends
   a command over the USB vendor channel and the firmware raises OnClick. The puck shows the command
   number because that number is the entire contract between the two sides. */
.vcl-embwebbutton { box-sizing:border-box; display:flex; align-items:center; justify-content:space-between;
  gap:6px; padding:0 8px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:5px; font:10px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embwebbutton-cap { font-weight:bold; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vcl-embwebbutton-cmd { font:9px Consolas,monospace; opacity:.7; flex:none; }
/* TEmbWifi */
/* v1023: TEmbWifi -- a design-time puck: three arcs over a dot, the radio's name under it. */
.vcl-embwifi { box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:2px; background:var(--tp-emb-bg,#f4f6f8);
  border:1px solid var(--tp-emb-border,#b9c2cc); border-radius:6px; font:9px Tahoma,sans-serif;
  color:var(--tp-fg,#1a1a1a); user-select:none; }
.vcl-embwifi-rune { font:bold 18px Tahoma,sans-serif; color:#1e6fd9; line-height:18px; }
.vcl-embwifi-name { letter-spacing:.3px; opacity:.75; }

/* TpAnnunciator */
/* TpAnnunciator -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-annunciator { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='28' width='92' height='44' rx='5' fill='%231c1511'/%3E%3Crect x='9' y='33' width='82' height='34' rx='4' fill='%23ef4444'/%3E%3Crect x='26' y='46' width='48' height='8' rx='2' fill='%23e6e8eb'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-annunciator canvas { display: block; width: 100%; height: 100%; }

/* TpArcLabel */
/* TpArcLabel -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-arclabel { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='none' stroke='%23302a24' stroke-width='2'/%3E%3Cpath id='t' d='M16 50 A34 34 0 0 1 84 50' fill='none'/%3E%3Ctext fill='%23e6e8eb' font-size='13' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E%3CtextPath href='%23t' startOffset='50%25' text-anchor='middle'%3ELABEL%3C/textPath%3E%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-arclabel canvas { display: block; width: 100%; height: 100%; }

/* TpBarChart */
/* TpBarChart -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-barchart { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg fill='%2360a5fa'%3E%3Crect x='6' y='50' width='12' height='44' rx='2'/%3E%3Crect x='24' y='34' width='12' height='60' rx='2'/%3E%3Crect x='42' y='58' width='12' height='36' rx='2'/%3E%3Crect x='78' y='40' width='12' height='54' rx='2'/%3E%3C/g%3E%3Crect x='60' y='14' width='12' height='80' rx='2' fill='%23f59e0b'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-barchart canvas { display: block; width: 100%; height: 100%; }

/* TpBarGauge */
/* TpBarGauge -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-bargauge { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='2' y='30' width='96' height='40' rx='10' fill='%23302a24'/%3E%3Crect x='2' y='30' width='62' height='40' rx='10' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-bargauge canvas { display: block; width: 100%; height: 100%; }

/* TpBattery */
/* TpBattery -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-battery { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='30' width='84' height='40' rx='6' fill='none' stroke='%239aa3ae' stroke-width='4'/%3E%3Crect x='88' y='42' width='8' height='16' fill='%239aa3ae'/%3E%3Crect x='10' y='36' width='52' height='28' fill='%2322c55e'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-battery canvas { display: block; width: 100%; height: 100%; }

/* TpButtonGrid */
/* TpButtonGrid -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-buttongrid { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg fill='%231c1511'%3E%3Crect x='4' y='24' width='28' height='22' rx='4'/%3E%3Crect x='68' y='24' width='28' height='22' rx='4'/%3E%3Crect x='4' y='52' width='28' height='22' rx='4'/%3E%3Crect x='36' y='52' width='28' height='22' rx='4'/%3E%3Crect x='68' y='52' width='28' height='22' rx='4'/%3E%3C/g%3E%3Crect x='36' y='24' width='28' height='22' rx='4' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-buttongrid canvas { display: block; width: 100%; height: 100%; }

/* TpCard */
/* TpCard -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-card { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='10' width='92' height='80' rx='8' fill='%231c1511'/%3E%3Crect x='4' y='10' width='4' height='80' fill='%2360a5fa'/%3E%3Crect x='14' y='18' width='40' height='6' rx='2' fill='%239aa3ae'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-card canvas { display: block; width: 100%; height: 100%; }
/* a container: the canvas is the ground, the children stand on it */
.vcl-instr-card > canvas { position: absolute; left: 0; top: 0; z-index: 0; }

/* TpClockFace */
/* TpClockFace -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-clockface { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='%231c1511' stroke='%23302a24' stroke-width='3'/%3E%3Cg stroke='%23e6e8eb' stroke-width='3'%3E%3Cline x1='50' y1='10' x2='50' y2='18'/%3E%3Cline x1='90' y1='50' x2='82' y2='50'/%3E%3Cline x1='50' y1='90' x2='50' y2='82'/%3E%3Cline x1='10' y1='50' x2='18' y2='50'/%3E%3C/g%3E%3Cpath d='M50 50 L30 36' stroke='%23e6e8eb' stroke-width='5'/%3E%3Cpath d='M50 50 L72 22' stroke='%23e6e8eb' stroke-width='3'/%3E%3Cpath d='M50 50 L50 86' stroke='%2360a5fa' stroke-width='2'/%3E%3Ccircle cx='50' cy='50' r='4' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-clockface canvas { display: block; width: 100%; height: 100%; }

/* TpColorWheel */
/* TpColorWheel -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-colorwheel { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cdefs%3E%3ClinearGradient id='h' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23ff0000'/%3E%3Cstop offset='0.33' stop-color='%2300ff00'/%3E%3Cstop offset='0.66' stop-color='%230000ff'/%3E%3Cstop offset='1' stop-color='%23ff00ff'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='50' cy='50' r='40' fill='none' stroke='url(%23h)' stroke-width='10'/%3E%3Crect x='33' y='33' width='34' height='34' fill='%233090f0'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-colorwheel canvas { display: block; width: 100%; height: 100%; }

/* TpCompass */
/* TpCompass -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-compass { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='%231c1511' stroke='%23403a33' stroke-width='4'/%3E%3Cpath d='M44 8 L56 8 L50 17 Z' fill='%23f59e0b'/%3E%3Ctext x='40' y='34' fill='%23ef4444' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3EN%3C/text%3E%3Ctext x='68' y='54' fill='%23e6e8eb' font-size='14' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3EE%3C/text%3E%3Ctext x='50' y='58' fill='%23e6e8eb' font-size='14' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E35&%23176;%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-compass canvas { display: block; width: 100%; height: 100%; }

/* TpDualGauge */
/* TpDualGauge -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-dualgauge { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='52' r='40' fill='none' stroke='%23302a24' stroke-width='8' stroke-dasharray='190 62' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='40' fill='none' stroke='%2360a5fa' stroke-width='8' stroke-dasharray='130 122' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='28' fill='none' stroke='%23302a24' stroke-width='7' stroke-dasharray='132 44' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='28' fill='none' stroke='%23f59e0b' stroke-width='7' stroke-dasharray='60 116' transform='rotate(135 50 52)'/%3E%3Ctext x='50' y='60' fill='%2360a5fa' font-size='20' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E72%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-dualgauge canvas { display: block; width: 100%; height: 100%; }

/* TpFlipClock */
/* TpFlipClock -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-flipclock { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg fill='%231c1511'%3E%3Crect x='4' y='28' width='20' height='44' rx='3'/%3E%3Crect x='26' y='28' width='20' height='44' rx='3'/%3E%3Crect x='54' y='28' width='20' height='44' rx='3'/%3E%3Crect x='76' y='28' width='20' height='44' rx='3'/%3E%3C/g%3E%3Cg fill='%23e6e8eb' font-size='26' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E%3Ctext x='14' y='59'%3E1%3C/text%3E%3Ctext x='36' y='59'%3E0%3C/text%3E%3Ctext x='64' y='59'%3E4%3C/text%3E%3Ctext x='86' y='59'%3E2%3C/text%3E%3C/g%3E%3Crect x='0' y='49' width='100' height='2' fill='%230b0e13'/%3E%3Ccircle cx='50' cy='42' r='2' fill='%23e6e8eb'/%3E%3Ccircle cx='50' cy='58' r='2' fill='%23e6e8eb'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-flipclock canvas { display: block; width: 100%; height: 100%; }

/* TpGauge */
/* TpGauge -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-gauge { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='52' r='34' fill='none' stroke='%23302a24' stroke-width='12' stroke-dasharray='160 54' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='34' fill='none' stroke='%2360a5fa' stroke-width='12' stroke-dasharray='100 114' transform='rotate(135 50 52)'/%3E%3Ctext x='50' y='60' fill='%23e6e8eb' font-size='22' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E63%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-gauge canvas { display: block; width: 100%; height: 100%; }

/* TpHalfGauge */
/* TpHalfGauge -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-halfgauge { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cpath d='M10 70 A40 40 0 0 1 90 70' fill='none' stroke='%23302a24' stroke-width='12'/%3E%3Cpath d='M10 70 A40 40 0 0 1 78 42' fill='none' stroke='%2360a5fa' stroke-width='12'/%3E%3Ctext x='50' y='68' fill='%23e6e8eb' font-size='20' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E63%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-halfgauge canvas { display: block; width: 100%; height: 100%; }

/* TpHeatGrid */
/* TpHeatGrid -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-heatgrid { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cdefs%3E%3CradialGradient id='g' cx='0.6' cy='0.35' r='0.6'%3E%3Cstop offset='0' stop-color='%23fff5a0'/%3E%3Cstop offset='0.35' stop-color='%23fca636'/%3E%3Cstop offset='0.6' stop-color='%23f04c3b'/%3E%3Cstop offset='0.8' stop-color='%23a6268f'/%3E%3Cstop offset='1' stop-color='%23000428'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect x='6' y='6' width='88' height='88' fill='url(%23g)'/%3E%3Ccircle cx='60' cy='36' r='6' fill='none' stroke='%23ffffff' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-heatgrid canvas { display: block; width: 100%; height: 100%; }

/* TpHorizon */
/* TpHorizon -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-horizon { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3CclipPath id='c'%3E%3Ccircle cx='50' cy='50' r='40'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23c)'%3E%3Crect x='0' y='0' width='100' height='100' fill='%2348a0e0'/%3E%3Cpath d='M0 62 L100 38 L100 100 L0 100 Z' fill='%238c5a30'/%3E%3C/g%3E%3Ccircle cx='50' cy='50' r='42' fill='none' stroke='%231c1511' stroke-width='5'/%3E%3Crect x='24' y='48' width='18' height='4' fill='%23f59e0b'/%3E%3Crect x='58' y='48' width='18' height='4' fill='%23f59e0b'/%3E%3Ccircle cx='50' cy='50' r='3' fill='%23f59e0b'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-horizon canvas { display: block; width: 100%; height: 100%; }

/* TpIcon */
/* TpIcon -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-icon { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='%23f59e0b' fill-opacity='0.15'/%3E%3Ccircle cx='50' cy='42' r='22' fill='%23f59e0b'/%3E%3Crect x='38' y='64' width='24' height='10' fill='%23f59e0b'/%3E%3Crect x='40' y='78' width='20' height='5' fill='%23f59e0b'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-icon canvas { display: block; width: 100%; height: 100%; }

/* TpImageAnim */
/* TpImageAnim -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-imageanim { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='6' y='20' width='88' height='60' rx='4' fill='%231c1511'/%3E%3Cg fill='%23302a24'%3E%3Crect x='10' y='24' width='6' height='6'/%3E%3Crect x='10' y='70' width='6' height='6'/%3E%3Crect x='84' y='24' width='6' height='6'/%3E%3Crect x='84' y='70' width='6' height='6'/%3E%3C/g%3E%3Cpath d='M42 36 L64 50 L42 64 Z' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-imageanim canvas { display: block; width: 100%; height: 100%; }

/* TpKnob */
/* TpKnob -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-knob { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='52' r='36' fill='none' stroke='%23302a24' stroke-width='9' stroke-dasharray='170 56' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='36' fill='none' stroke='%2360a5fa' stroke-width='9' stroke-dasharray='100 126' transform='rotate(135 50 52)'/%3E%3Ccircle cx='50' cy='52' r='26' fill='%231c1511'/%3E%3Ccircle cx='82' cy='40' r='7' fill='%23e6e8eb' stroke='%2360a5fa' stroke-width='3'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-knob canvas { display: block; width: 100%; height: 100%; }

/* TpLamp */
/* TpLamp -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-lamp { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='40' fill='%23234e22'/%3E%3Ccircle cx='50' cy='50' r='30' fill='%2322c55e'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-lamp canvas { display: block; width: 100%; height: 100%; }

/* TpLevelMeter */
/* TpLevelMeter -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-levelmeter { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg%3E%3Crect x='35' y='80' width='30' height='8' fill='%2322c55e'/%3E%3Crect x='35' y='68' width='30' height='8' fill='%2322c55e'/%3E%3Crect x='35' y='56' width='30' height='8' fill='%2322c55e'/%3E%3Crect x='35' y='44' width='30' height='8' fill='%23f59e0b' fill-opacity='0.3'/%3E%3Crect x='35' y='32' width='30' height='8' fill='%23f59e0b'/%3E%3Crect x='35' y='20' width='30' height='8' fill='%23ef4444' fill-opacity='0.3'/%3E%3Crect x='35' y='8' width='30' height='8' fill='%23ef4444' fill-opacity='0.3'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-levelmeter canvas { display: block; width: 100%; height: 100%; }

/* TpLightRow */
/* TpLightRow -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-lightrow { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg%3E%3Ccircle cx='10' cy='50' r='7' fill='%2322c55e'/%3E%3Ccircle cx='30' cy='50' r='7' fill='%2322c55e'/%3E%3Ccircle cx='50' cy='50' r='7' fill='%2322c55e'/%3E%3Ccircle cx='70' cy='50' r='7' fill='%23f59e0b'/%3E%3Ccircle cx='90' cy='50' r='7' fill='%23403a33'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-lightrow canvas { display: block; width: 100%; height: 100%; }

/* TpLineMeter */
/* TpLineMeter -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-linemeter { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg stroke-width='5' stroke-linecap='butt'%3E%3Cline x1='16' y1='78' x2='24' y2='72' stroke='%2360a5fa'/%3E%3Cline x1='10' y1='56' x2='20' y2='54' stroke='%2360a5fa'/%3E%3Cline x1='14' y1='33' x2='23' y2='38' stroke='%2360a5fa'/%3E%3Cline x1='29' y1='15' x2='35' y2='24' stroke='%2360a5fa'/%3E%3Cline x1='50' y1='8' x2='50' y2='19' stroke='%2360a5fa'/%3E%3Cline x1='71' y1='15' x2='65' y2='24' stroke='%2360a5fa'/%3E%3Cline x1='86' y1='33' x2='77' y2='38' stroke='%23f59e0b'/%3E%3Cline x1='90' y1='56' x2='80' y2='54' stroke='%23302a24'/%3E%3Cline x1='84' y1='78' x2='76' y2='72' stroke='%23302a24'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-linemeter canvas { display: block; width: 100%; height: 100%; }

/* TpMarquee */
/* TpMarquee -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-marquee { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='2' y='32' width='96' height='36' rx='4' fill='%231c1511'/%3E%3Cg fill='%23f59e0b'%3E%3Ccircle cx='12' cy='40' r='2'/%3E%3Ccircle cx='12' cy='45' r='2'/%3E%3Ccircle cx='12' cy='50' r='2'/%3E%3Ccircle cx='12' cy='55' r='2'/%3E%3Ccircle cx='12' cy='60' r='2'/%3E%3Ccircle cx='17' cy='50' r='2'/%3E%3Ccircle cx='22' cy='40' r='2'/%3E%3Ccircle cx='22' cy='45' r='2'/%3E%3Ccircle cx='22' cy='50' r='2'/%3E%3Ccircle cx='22' cy='55' r='2'/%3E%3Ccircle cx='22' cy='60' r='2'/%3E%3Ccircle cx='32' cy='40' r='2'/%3E%3Ccircle cx='32' cy='60' r='2'/%3E%3Ccircle cx='37' cy='40' r='2'/%3E%3Ccircle cx='37' cy='45' r='2'/%3E%3Ccircle cx='37' cy='50' r='2'/%3E%3Ccircle cx='37' cy='55' r='2'/%3E%3Ccircle cx='37' cy='60' r='2'/%3E%3Ccircle cx='42' cy='40' r='2'/%3E%3Ccircle cx='42' cy='60' r='2'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-marquee canvas { display: block; width: 100%; height: 100%; }

/* TpMeter */
/* TpMeter -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-meter { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='%231c1511'/%3E%3Cg stroke='%23e6e8eb' stroke-width='2'%3E%3Cline x1='50' y1='8' x2='50' y2='16'/%3E%3Cline x1='18' y1='72' x2='25' y2='68'/%3E%3Cline x1='82' y1='72' x2='75' y2='68'/%3E%3Cline x1='20' y1='36' x2='27' y2='40'/%3E%3Cline x1='80' y1='36' x2='73' y2='40'/%3E%3C/g%3E%3Cpath d='M50 50 L72 24' stroke='%23ef4444' stroke-width='3'/%3E%3Ccircle cx='50' cy='50' r='5' fill='%23e6e8eb'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-meter canvas { display: block; width: 100%; height: 100%; }

/* TpNumPad */
/* TpNumPad -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-numpad { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='10' y='4' width='80' height='18' rx='3' fill='%231c1511'/%3E%3Cg fill='%23302a24'%3E%3Crect x='10' y='26' width='24' height='15' rx='3'/%3E%3Crect x='38' y='26' width='24' height='15' rx='3'/%3E%3Crect x='66' y='26' width='24' height='15' rx='3'/%3E%3Crect x='10' y='44' width='24' height='15' rx='3'/%3E%3Crect x='38' y='44' width='24' height='15' rx='3'/%3E%3Crect x='66' y='44' width='24' height='15' rx='3'/%3E%3Crect x='10' y='62' width='24' height='15' rx='3'/%3E%3Crect x='38' y='62' width='24' height='15' rx='3'/%3E%3Crect x='66' y='62' width='24' height='15' rx='3'/%3E%3Crect x='10' y='80' width='24' height='15' rx='3'/%3E%3Crect x='38' y='80' width='24' height='15' rx='3'/%3E%3C/g%3E%3Crect x='66' y='80' width='24' height='15' rx='3' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-numpad canvas { display: block; width: 100%; height: 100%; }

/* TpPager */
/* IDE v1110: TpPager -- a page control without tabs (dashboard-plan.txt #55); its pages are TTabSheets. */
.vcl-pager {
  box-sizing: border-box;
  overflow: hidden;
  background: var(--tp-pager-bg, #0b0e13);
}
.vcl-pager > .vcl-tabsheet { box-sizing: border-box; }
.vcl-pager > .tp-pager-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 18px;
  z-index: 2;
}
.vcl-pager > .tp-pager-dots > .tp-pager-dot {
  position: absolute;
  border-radius: 50%;
  cursor: pointer;
}
/* the design canvas (client.js lays the pages out at run time): the dots' place, as a sketch */
.vcl-pager:not([data-tp-pg-wired])::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 5px;
  width: 44px;
  margin-left: -22px;
  height: 8px;
  background: radial-gradient(circle, #60a5fa 3px, transparent 4px) 0 0 / 11px 8px repeat-x;
  opacity: .7;
}

/* TpPie */
/* TpPie -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pie { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='34' fill='none' stroke='%2360a5fa' stroke-width='20' stroke-dasharray='60 154'/%3E%3Ccircle cx='50' cy='50' r='34' fill='none' stroke='%2322c55e' stroke-width='20' stroke-dasharray='0 60 70 84'/%3E%3Ccircle cx='50' cy='50' r='34' fill='none' stroke='%23f59e0b' stroke-width='20' stroke-dasharray='0 130 84 0'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pie canvas { display: block; width: 100%; height: 100%; }

/* TpPill */
/* TpPill -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pill { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='2' y='30' width='96' height='40' rx='20' fill='%2322c55e' fill-opacity='0.3'/%3E%3Ccircle cx='22' cy='50' r='7' fill='%2322c55e'/%3E%3Crect x='36' y='45' width='46' height='10' rx='3' fill='%23e6e8eb'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pill canvas { display: block; width: 100%; height: 100%; }

/* TpPipe */
/* TpPipe -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pipe { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='36' width='92' height='28' fill='%239aa3ae'/%3E%3Crect x='4' y='42' width='92' height='16' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pipe canvas { display: block; width: 100%; height: 100%; }

/* TpPressure */
/* TpPressure -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pressure { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='46' fill='%23b4b8bc'/%3E%3Ccircle cx='50' cy='50' r='40' fill='%23eef0f0'/%3E%3Cpath d='M80 66 A34 34 0 0 0 84 46' fill='none' stroke='%23ef4444' stroke-width='5'/%3E%3Cpath d='M50 50 L68 22' stroke='%23161a20' stroke-width='3'/%3E%3Ccircle cx='50' cy='50' r='5' fill='%23161a20'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pressure canvas { display: block; width: 100%; height: 100%; }

/* TpPump */
/* TpPump -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pump { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='56' y='10' width='38' height='12' fill='%239aa3ae'/%3E%3Ccircle cx='46' cy='50' r='34' fill='%231c1511' stroke='%239aa3ae' stroke-width='6'/%3E%3Cpath d='M34 30 L70 50 L34 70 Z' fill='%2322c55e'/%3E%3Cpath d='M24 96 L34 80 L58 80 L68 96 Z' fill='%239aa3ae'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pump canvas { display: block; width: 100%; height: 100%; }

/* TpPushButton */
/* TpPushButton -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-pushbutton { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='%239aa3ae'/%3E%3Ccircle cx='50' cy='50' r='38' fill='%231c1511'/%3E%3Ccircle cx='50' cy='50' r='30' fill='%2322c55e'/%3E%3Ccircle cx='40' cy='40' r='6' fill='%23ffffff' fill-opacity='0.35'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-pushbutton canvas { display: block; width: 100%; height: 100%; }

/* TpRadar */
/* TpRadar -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-radar { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cpath d='M8 76 A42 42 0 0 1 92 76 Z' fill='%231c1511'/%3E%3Cg fill='none' stroke='%23225c3a' stroke-width='1.5'%3E%3Cpath d='M22 76 A28 28 0 0 1 78 76'/%3E%3Cpath d='M36 76 A14 14 0 0 1 64 76'/%3E%3Cline x1='8' y1='76' x2='92' y2='76'/%3E%3Cline x1='50' y1='76' x2='50' y2='34'/%3E%3C/g%3E%3Cline x1='50' y1='76' x2='22' y2='46' stroke='%2322c55e' stroke-width='3'/%3E%3Ccircle cx='66' cy='52' r='3' fill='%23ef4444'/%3E%3Ccircle cx='30' cy='62' r='3' fill='%23ef4444'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-radar canvas { display: block; width: 100%; height: 100%; }

/* TpRoller */
/* TpRoller -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-roller { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='20' y='4' width='60' height='92' rx='8' fill='%231c1511'/%3E%3Crect x='22' y='38' width='56' height='24' fill='%23243a5a'/%3E%3Cg fill='%23e6e8eb' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E%3Ctext x='50' y='22' font-size='12' fill-opacity='0.4'%3ETUE%3C/text%3E%3Ctext x='50' y='56' font-size='16'%3EWED%3C/text%3E%3Ctext x='50' y='84' font-size='12' fill-opacity='0.4'%3ETHU%3C/text%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-roller canvas { display: block; width: 100%; height: 100%; }

/* TpSectorClock */
/* TpSectorClock -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-sectorclock { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='44' fill='none' stroke='%23302a24' stroke-width='6' stroke-dasharray='3 1.6'/%3E%3Ccircle cx='50' cy='50' r='28' fill='none' stroke='%2360a5fa' stroke-width='14' stroke-dasharray='110 66'/%3E%3Ccircle cx='50' cy='50' r='28' fill='none' stroke='%23302a24' stroke-width='14' stroke-dasharray='0 112 64 0'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-sectorclock canvas { display: block; width: 100%; height: 100%; }

/* TpSelector */
/* TpSelector -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-selector { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='58' r='26' fill='%23302a24'/%3E%3Ccircle cx='50' cy='58' r='23' fill='%231c1511'/%3E%3Crect x='44' y='34' width='12' height='48' rx='3' fill='%239aa3ae'/%3E%3Crect x='48' y='36' width='4' height='22' fill='%2360a5fa'/%3E%3Ctext x='22' y='22' fill='%239aa3ae' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E0%3C/text%3E%3Ctext x='45' y='14' fill='%2360a5fa' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E1%3C/text%3E%3Ctext x='70' y='22' fill='%239aa3ae' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E2%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-selector canvas { display: block; width: 100%; height: 100%; }

/* TpSetpoint */
/* TpSetpoint -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-setpoint { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='10' y='8' width='18' height='84' rx='2' fill='%23302a24'/%3E%3Crect x='10' y='44' width='18' height='48' fill='%2360a5fa'/%3E%3Crect x='6' y='34' width='26' height='4' fill='%23e6e8eb'/%3E%3Ctext x='38' y='38' fill='%2360a5fa' font-size='28' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E52%3C/text%3E%3Ctext x='42' y='62' fill='%239aa3ae' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E60%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-setpoint canvas { display: block; width: 100%; height: 100%; }

/* TpSevenSeg */
/* TpSevenSeg -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-sevenseg { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg fill='%23ef4444' font-family='DejaVu Sans Mono,monospace' font-size='60' font-weight='bold'%3E%3Ctext x='4' y='78'%3E88.8%3C/text%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-sevenseg canvas { display: block; width: 100%; height: 100%; }

/* TpSignal */
/* TpSignal -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-signal { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='30' y='4' width='40' height='92' rx='8' fill='%231c1511'/%3E%3Ccircle cx='50' cy='22' r='11' fill='%23ef4444' fill-opacity='0.3'/%3E%3Ccircle cx='50' cy='50' r='11' fill='%23f59e0b' fill-opacity='0.3'/%3E%3Ccircle cx='50' cy='78' r='11' fill='%2322c55e'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-signal canvas { display: block; width: 100%; height: 100%; }

/* TpSlider */
/* TpSlider -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-slider { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='6' y='46' width='88' height='8' rx='4' fill='%23302a24'/%3E%3Crect x='6' y='46' width='56' height='8' rx='4' fill='%2360a5fa'/%3E%3Crect x='54' y='32' width='16' height='36' rx='4' fill='%23d4d7db'/%3E%3Crect x='61' y='36' width='2' height='28' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-slider canvas { display: block; width: 100%; height: 100%; }

/* TpSpark */
/* TpSpark -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-spark { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cpath d='M4 70 L20 52 L34 60 L50 30 L66 42 L80 22 L94 28 L94 90 L4 90 Z' fill='%2360a5fa' fill-opacity='0.25'/%3E%3Cpath d='M4 70 L20 52 L34 60 L50 30 L66 42 L80 22 L94 28' fill='none' stroke='%2360a5fa' stroke-width='4'/%3E%3Ccircle cx='94' cy='28' r='5' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-spark canvas { display: block; width: 100%; height: 100%; }

/* TpSpeedo */
/* TpSpeedo -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-speedo { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='54' r='40' fill='none' stroke='%23302a24' stroke-width='6' stroke-dasharray='167 84' transform='rotate(150 50 54)'/%3E%3Ccircle cx='50' cy='54' r='40' fill='none' stroke='%2360a5fa' stroke-width='6' stroke-dasharray='100 151' transform='rotate(150 50 54)'/%3E%3Cpath d='M50 54 L74 30' stroke='%2360a5fa' stroke-width='3'/%3E%3Ccircle cx='50' cy='54' r='5' fill='%231c1511'/%3E%3Ctext x='50' y='84' fill='%23e6e8eb' font-size='16' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E96%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-speedo canvas { display: block; width: 100%; height: 100%; }

/* TpSpider */
/* TpSpider -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-spider { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cg fill='none' stroke='%2347536b' stroke-width='2'%3E%3Cpath d='M50 8 L88 30 L88 72 L50 94 L12 72 L12 30 Z'/%3E%3Cpath d='M50 30 L69 41 L69 61 L50 72 L31 61 L31 41 Z'/%3E%3C/g%3E%3Cpath d='M50 16 L80 36 L72 70 L50 64 L22 66 L26 34 Z' fill='%2360a5fa' fill-opacity='0.35' stroke='%2360a5fa' stroke-width='3'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-spider canvas { display: block; width: 100%; height: 100%; }

/* TpSpinner */
/* TpSpinner -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-spinner { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='34' fill='none' stroke='%23302a24' stroke-width='10'/%3E%3Cpath d='M50 16 A34 34 0 0 1 84 50' fill='none' stroke='%2360a5fa' stroke-width='10'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-spinner canvas { display: block; width: 100%; height: 100%; }

/* TpStat */
/* TpStat -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-stat { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ctext x='6' y='64' fill='%2360a5fa' font-size='48' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E42%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-stat canvas { display: block; width: 100%; height: 100%; }

/* TpStepper */
/* TpStepper -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-stepper { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='2' y='30' width='96' height='40' rx='8' fill='%231c1511'/%3E%3Crect x='6' y='34' width='30' height='32' rx='6' fill='%23302a24'/%3E%3Crect x='64' y='34' width='30' height='32' rx='6' fill='%23302a24'/%3E%3Crect x='13' y='48' width='16' height='4' fill='%23e6e8eb'/%3E%3Crect x='71' y='48' width='16' height='4' fill='%23e6e8eb'/%3E%3Crect x='77' y='42' width='4' height='16' fill='%23e6e8eb'/%3E%3Ctext x='50' y='57' fill='%2360a5fa' font-size='18' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E5%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-stepper canvas { display: block; width: 100%; height: 100%; }

/* TpSwitch */
/* TpSwitch -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-switch { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='32' width='64' height='36' rx='18' fill='%2322c55e'/%3E%3Ccircle cx='50' cy='50' r='14' fill='%23e6e8eb'/%3E%3Crect x='74' y='45' width='22' height='10' rx='3' fill='%23e6e8eb'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-switch canvas { display: block; width: 100%; height: 100%; }

/* TpTank */
/* TpTank -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-tank { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='25' y='6' width='50' height='88' rx='14' fill='%239aa3ae'/%3E%3Crect x='28' y='9' width='44' height='82' rx='12' fill='%23302a24'/%3E%3Crect x='28' y='44' width='44' height='47' rx='12' fill='%2360a5fa'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-tank canvas { display: block; width: 100%; height: 100%; }

/* TpThermometer */
/* TpThermometer -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-thermometer { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='22' y='4' width='18' height='70' rx='9' fill='%239aa3ae'/%3E%3Ccircle cx='31' cy='80' r='15' fill='%239aa3ae'/%3E%3Crect x='25' y='30' width='12' height='50' rx='6' fill='%2360a5fa'/%3E%3Ccircle cx='31' cy='80' r='12' fill='%2360a5fa'/%3E%3Ctext x='50' y='30' fill='%2360a5fa' font-size='24' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E63%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-thermometer canvas { display: block; width: 100%; height: 100%; }

/* TpTileView */
/* TpTileView -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-tileview { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='8' y='8' width='84' height='84' rx='10' fill='%2355421c'/%3E%3Ccircle cx='30' cy='30' r='10' fill='%23f59e0b'/%3E%3Crect x='25' y='41' width='10' height='5' fill='%23f59e0b'/%3E%3Ctext x='18' y='82' fill='%23e6e8eb' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3EON%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-tileview canvas { display: block; width: 100%; height: 100%; }

/* TpTimerRing */
/* TpTimerRing -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-timerring { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Ccircle cx='50' cy='50' r='40' fill='none' stroke='%23302a24' stroke-width='10'/%3E%3Cpath d='M50 10 A40 40 0 1 1 15 70' fill='none' stroke='%2360a5fa' stroke-width='10' stroke-linecap='round'/%3E%3Ctext x='50' y='58' fill='%23e6e8eb' font-size='22' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E4:12%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-timerring canvas { display: block; width: 100%; height: 100%; }

/* TpTopBar */
/* TpTopBar -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-topbar { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='0' y='30' width='100' height='40' fill='%231c1511'/%3E%3Crect x='6' y='44' width='34' height='10' rx='3' fill='%23e6e8eb'/%3E%3Cg fill='%23e6e8eb'%3E%3Crect x='56' y='56' width='3' height='4'/%3E%3Crect x='61' y='52' width='3' height='8'/%3E%3Crect x='66' y='48' width='3' height='12'/%3E%3C/g%3E%3Crect x='74' y='46' width='20' height='12' rx='2' fill='none' stroke='%23e6e8eb' stroke-width='2'/%3E%3Crect x='77' y='49' width='12' height='6' fill='%2322c55e'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-topbar canvas { display: block; width: 100%; height: 100%; }

/* TpTrend */
/* TpTrend -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-trend { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='10' width='70' height='80' fill='%231c1511'/%3E%3Cpath d='M4 50 L74 50' stroke='%23f59e0b' stroke-width='3' stroke-dasharray='6 4'/%3E%3Cpath d='M6 80 L16 66 L24 72 L34 54 L44 60 L54 40 L64 46 L72 30' fill='none' stroke='%2360a5fa' stroke-width='4'/%3E%3Ctext x='78' y='44' fill='%2360a5fa' font-size='16' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E70%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-trend canvas { display: block; width: 100%; height: 100%; }

/* TpValve */
/* TpValve -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-valve { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Cpath d='M10 50 L50 70 L50 30 Z M90 50 L50 70 L50 30 Z' fill='%2322c55e' transform='rotate(90 50 50)'/%3E%3Cpath d='M14 30 L50 50 L14 70 Z M86 30 L50 50 L86 70 Z' fill='%2322c55e'/%3E%3Crect x='48' y='14' width='4' height='36' fill='%239aa3ae'/%3E%3Crect x='30' y='8' width='40' height='8' rx='4' fill='%239aa3ae'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-valve canvas { display: block; width: 100%; height: 100%; }

/* TpVuMeter */
/* TpVuMeter -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-vumeter { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='20' width='92' height='60' rx='5' fill='%23f0d890'/%3E%3Cpath d='M20 58 Q50 30 80 58' fill='none' stroke='%23161a20' stroke-width='2'/%3E%3Cpath d='M66 46 Q74 50 80 58' fill='none' stroke='%23ef4444' stroke-width='4'/%3E%3Cpath d='M50 96 L64 40' stroke='%23161a20' stroke-width='2'/%3E%3Ccircle cx='86' cy='28' r='3' fill='%23ef4444'/%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-vumeter canvas { display: block; width: 100%; height: 100%; }

/* TpWeatherTile */
/* TpWeatherTile -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-weathertile { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='2' y='14' width='96' height='72' rx='8' fill='%231c1511'/%3E%3Ccircle cx='28' cy='50' r='10' fill='%23f59e0b'/%3E%3Cg stroke='%23f59e0b' stroke-width='3'%3E%3Cline x1='28' y1='30' x2='28' y2='34'/%3E%3Cline x1='28' y1='66' x2='28' y2='70'/%3E%3Cline x1='8' y1='50' x2='12' y2='50'/%3E%3Cline x1='44' y1='50' x2='48' y2='50'/%3E%3C/g%3E%3Ctext x='74' y='58' fill='%23e6e8eb' font-size='20' font-weight='bold' text-anchor='middle' font-family='DejaVu Sans,Verdana,sans-serif'%3E21&%23176;%3C/text%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-weathertile canvas { display: block; width: 100%; height: 100%; }

/* TpWordClock */
/* TpWordClock -- the host carries the geometry (inline {Style}); client.js draws the <canvas>. On /design (no
   client.js) the canvas stays empty and this sketch shows the instrument's shape on the dark ground. */
.vcl-instr-wordclock { box-sizing: border-box; position: relative; overflow: hidden; background: #0b0e13 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid meet'%3E%3Crect x='4' y='4' width='92' height='92' rx='6' fill='%231c1511'/%3E%3Cg font-size='11' font-weight='bold' font-family='DejaVu Sans,Verdana,sans-serif'%3E%3Ctext x='10' y='22' fill='%23f59e0b'%3EIT IS%3C/text%3E%3Ctext x='48' y='22' fill='%23403a33'%3EASAMPM%3C/text%3E%3Ctext x='10' y='40' fill='%23403a33'%3EACQUARTER%3C/text%3E%3Ctext x='10' y='58' fill='%23f59e0b'%3ETWENTYFIVE%3C/text%3E%3Ctext x='10' y='76' fill='%23f59e0b'%3EPAST%3C/text%3E%3Ctext x='48' y='76' fill='%23403a33'%3EERUNI%3C/text%3E%3Ctext x='10' y='92' fill='%23f59e0b'%3ETEN%3C/text%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.vcl-instr-wordclock canvas { display: block; width: 100%; height: 100%; }

/* TDatabase */
/* v048: shared NON-VISUAL component "puck" — the design-time icon for data comps that have no icon
   image of their own (TMySQLDatabase, TMySQLQuery, and any future Data-tab non-visual comp). It mirrors
   the reference IDE's default placeholder puck: a muted dashed tile with a small centred box glyph.

   IMPORTANT: the comp's BOX (its width/height + selection bounds) is ONLY the 24x24 puck. The instance
   name is rendered as an ::after label that's absolutely positioned BELOW the puck (out of flow), so it
   never enlarges the box. Both Data-tab styles.css files load together via /components.css, so this base
   rule is always present even if only one comp is on the form. Hidden by default (display:none) so the
   RUNNING app never shows the puck; /design injects `.vcl-nonvisual { display:block !important }` (see
   ServeDesign) to reveal it. The name comes from data-tp-name via ::after — NOT textContent — so the
   worker's FCaption='' boot delta can't wipe it. */
.vcl-nonvisual {
  display: none;
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
  overflow: visible;
  /* the 24x24 placeholder puck — dashed tile (#E4E7EC fill, #969CA5 dashed border) + centred box glyph
     (#C4C9D0 fill, #787E88 border). An inline SVG so it needs no image file (matches the ref's internally
     drawn placeholder.png). Painted on the box itself: the box IS the puck. */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect x='.5' y='.5' width='23' height='23' fill='%23E4E7EC' stroke='%23969CA5' stroke-dasharray='3 2'/%3E%3Crect x='8' y='8' width='8' height='8' fill='%23C4C9D0' stroke='%23787E88'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
}
/* the instance name, floated BELOW the puck box (absolute → does not affect the box size/selection). */
.vcl-nonvisual::after {
  content: attr(data-tp-name);
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 2px;
  font: 11px/1.2 Tahoma, "Segoe UI", sans-serif;
  color: #000;
  white-space: nowrap;
  pointer-events: none;
}

/* TMySQLDatabase */
/* v048: shared NON-VISUAL component "puck" — the design-time icon for data comps that have no icon
   image of their own (TMySQLDatabase, TMySQLQuery, and any future Data-tab non-visual comp). It mirrors
   the reference IDE's default placeholder puck: a muted dashed tile with a small centred box glyph.

   IMPORTANT: the comp's BOX (its width/height + selection bounds) is ONLY the 24x24 puck. The instance
   name is rendered as an ::after label that's absolutely positioned BELOW the puck (out of flow), so it
   never enlarges the box. Both Data-tab styles.css files load together via /components.css, so this base
   rule is always present even if only one comp is on the form. Hidden by default (display:none) so the
   RUNNING app never shows the puck; /design injects `.vcl-nonvisual { display:block !important }` (see
   ServeDesign) to reveal it. The name comes from data-tp-name via ::after — NOT textContent — so the
   worker's FCaption='' boot delta can't wipe it. */
.vcl-nonvisual {
  display: none;
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
  overflow: visible;
  /* the 24x24 placeholder puck — dashed tile (#E4E7EC fill, #969CA5 dashed border) + centred box glyph
     (#C4C9D0 fill, #787E88 border). An inline SVG so it needs no image file (matches the ref's internally
     drawn placeholder.png). Painted on the box itself: the box IS the puck. */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Crect x='.5' y='.5' width='23' height='23' fill='%23E4E7EC' stroke='%23969CA5' stroke-dasharray='3 2'/%3E%3Crect x='8' y='8' width='8' height='8' fill='%23C4C9D0' stroke='%23787E88'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
}
/* the instance name, floated BELOW the puck box (absolute → does not affect the box size/selection). */
.vcl-nonvisual::after {
  content: attr(data-tp-name);
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 2px;
  font: 11px/1.2 Tahoma, "Segoe UI", sans-serif;
  color: #000;
  white-space: nowrap;
  pointer-events: none;
}

/* TMySQLDirectQuery */
/* v075: TMySQLDirectQuery uses the shared non-visual "puck" — its styling (the dashed placeholder tile +
   centred name) lives in components/Data/TMySQLDatabase/styles.css under .vcl-nonvisual. No per-comp icon:
   the data comps share the default puck and are distinguished by their name label. */

/* TActionMainMenuBar */
/* v728. The action-driven main menu bar. A horizontal strip of top-level
   headers; each opens a dropdown built from the tree the host sends. The look
   follows the IDE's own bar (flat, no XP connector strip, dropdown a few px
   BELOW the header) -- which is what mainUnit's TFormMenuPopup override exists
   to achieve in the Delphi build, done here in CSS instead of in NCPaint. */
.vcl-actionmainmenubar {
  box-sizing: border-box;
  background: var(--tp-menu-bg, #f0f0f0);
  color: var(--tp-menu-fg, #000);
  display: flex;
  align-items: stretch;
  font: 12px Tahoma, "Segoe UI", sans-serif;
  user-select: none;
  position: relative;
}
.vcl-amb-root { display: flex; align-items: stretch; }
.vcl-amb-top {
  padding: 0 11px;
  display: flex;
  align-items: center;
  white-space: pre;      /* mainUnit pads captions with spaces to widen the gaps */
  cursor: default;
}
.vcl-amb-top:hover,
.vcl-amb-top.tp-open { background: var(--tp-menu-hot, #cce8ff); }
.vcl-amb-pop {
  position: absolute;
  top: 100%;             /* BELOW the header, never overlapping it */
  min-width: 180px;
  background: var(--tp-menu-bg, #fff);
  border: 1px solid var(--tp-menu-border, #a0a0a0);
  box-shadow: none;      /* DisplayShadow is overridden away in the IDE too */
  padding: 2px 0;
  z-index: 40;
}
.vcl-amb-item {
  padding: 3px 24px 3px 22px;
  display: flex;
  justify-content: space-between;
  gap: 24px;
  cursor: default;
}
.vcl-amb-item:hover { background: var(--tp-menu-hot, #cce8ff); }
.vcl-amb-item.tp-disabled { color: var(--tp-menu-disabled, #9a9a9a); }
.vcl-amb-item.tp-disabled:hover { background: transparent; }
.vcl-amb-item .tp-short { color: var(--tp-menu-disabled, #808080); }
.vcl-amb-sep { height: 1px; margin: 3px 8px; background: var(--tp-menu-border, #d0d0d0); }
.vcl-amb-sub::after { content: "\25B8"; margin-left: auto; padding-left: 12px; }

/* TButton */
/* v376 (port tsw2 v305, css-import.txt M3/S4): colour literals flow through the --tp-* theme tokens; every
   fallback is the exact pre-v376 literal, so with no token theme the look is pixel-identical. W's button base
   is a single background gradient (no solid base + gradient-image split like tsw2), so it maps to the base
   gradient token --tp-grad-button; --tp-btn-bg (a solid) is consumed by the header sign-in buttons instead. */
.vcl-button {
  cursor: pointer;
  border: 1px solid transparent;            /* transparent ring for the inset look */
  border-radius: 3px;
  background: var(--tp-grad-button, linear-gradient(to bottom, #f5f5f5, #dcdcdc));
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px var(--tp-btn-ring, rgb(173, 173, 173));
  color: var(--tp-btn-fg, #1a1a1a);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-button:hover:not(:disabled) {
  background: var(--tp-grad-button-hover, linear-gradient(to bottom, #e8f0fe, #c2d4f8));
  box-shadow: inset 0 0 0 1px var(--tp-accent, #4a7abf);
}
.vcl-button:active:not(:disabled) {
  background: var(--tp-grad-button-active, linear-gradient(to bottom, #c2d4f8, #e8f0fe));
}
.vcl-button:disabled { cursor: not-allowed; opacity: 0.55; }

/* v751: ...and neither does a BUTTON caption. Same measurement, same dialog:
   "Upload to Backend" and "Wipe remote htdocs first" each broke in two inside a
   button the .dfm sized for one line. */
.vcl-button { white-space: nowrap; }

/* TCheckBox */
/* TCheckBox — box + caption laid out in a flex row (from the reference classic.css). The geometry
   mirror sizes the <label> (data-tp-name host); the <input> is sized in em so it tracks the font. */
.vcl-checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
}
.vcl-checkbox > input {
  margin: 0;
  font: inherit;
  width: 1em !important;
  height: 1em !important;
  flex: 0 0 auto;
}

/* v751: a VCL checkbox caption is one line, like a label's -- see TLabel's note.
   Measured on Export to Web: "Wipe remote htdocs first" broke in two. */
.vcl-checkbox { white-space: nowrap; }
/* v1047: State = cbGrayed -- checked and dimmed (a checkbox's indeterminate state is a script property, not markup);
   Alignment = taLeftJustify -- the caption on the left, the box at the right edge, as the VCL draws it */
.vcl-checkbox > input[data-tp-grayed] { opacity: .45; }
.vcl-checkbox[data-tp-lefttext] { flex-direction: row-reverse; justify-content: space-between; }

/* TComboBox */
/* TComboBox — a plain (non-editable, csDropDownList-style) dropdown. The {ItemsOptions} token renders its
   <option>s server-side, selected by the DFM's ItemIndex; runtime selection changes mirror live (v313). */
.vcl-combobox {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* fallback — {Style} FontCss overrides when the model sets font */
  color: #000;
  padding: 1px 3px;
}

/* TEdit */
/* v376 (port tsw2 v305): theme tokens, fallbacks = pre-v376 literals. NB a real dropped TEdit also gets an
   inline background/border from BevelCssFromDfm (routed through --tp-def-tedit-*), which beats these; these
   remain the fallback for any TEdit that does not go through the surface path. */
.vcl-edit {
  background: var(--tp-input-bg, #fff);
  border: 1px solid var(--tp-input-border, rgb(173, 173, 173));
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}

/* TGroupBox */
/* TGroupBox — bordered group with the caption in the <legend> (from the reference classic.css). */
.vcl-groupbox {
  border: 1px solid var(--tp-border, #888);   /* v376: theme token */
  background: transparent;
  margin: 0;
  padding: 0;
}
.vcl-groupbox > legend {
  padding: 0 4px;
  font: inherit;
}

/* v884 (sqlconvert MERGE 44): *** A LEGEND IS IN THE FLOW AND A VCL CAPTION IS
   NOT. *** A fieldset's content box starts BELOW its legend, so a caption too
   long for the box wraps and pushes everything down. MEASURED on a 200px group
   with a 61-character caption: the legend went 16px -> 48px (three lines) and
   the first radio moved 32px down; sqlconvert measured 44px on their own form
   and the third radio out of the box. A VCL group-box caption sits on the
   border, is CLIPPED when it does not fit, and moves nothing.
     nowrap + overflow:hidden is the whole fix, and it is deliberately not
   absolute positioning: the content box still starts below the caption, which
   is what the VCL does too. Only the WRAPPING was wrong. A caption that fits
   is unaffected, which is why the golden corpus does not move. */
.vcl-groupbox > legend {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
  max-width: 100%;
  box-sizing: border-box;
}

/* v1057 (the user: "i see clipping at bottom of frame"): *** A CHILD'S Top IS
   MEASURED FROM THE BOX'S TOP, NOT FROM UNDER THE CAPTION. *** The VCL's group
   box is one window whose client area is the whole box: a child at Top = 0
   sits on the caption line (AdjustClientRect moves only ALIGNED children), and
   the native TpVcl build does the same. A fieldset's content box starts under
   its legend, so every placed child here sat a caption's height (16px at 13px
   Tahoma) lower than the .dfm says, and the last row fell out of a box sized
   for it. MEASURED on Project Options: a label at Top 33 rendered 49px under
   the frame's top. The legend is now exactly --tp-gb-cap tall and a placed
   child (data-tp-align alNone) is lifted by it, less the 1px border the
   designer's compRectForm counts on both axes -- so a drag measures what the
   .dfm stores. An ALIGNED child keeps the content box, as in the VCL. The
   property is registered so it resolves in the box's own font, not the
   child's. */
@property --tp-gb-cap { syntax: '<length>'; inherits: true; initial-value: 16px; }
.vcl-groupbox { --tp-gb-cap: 1.25em; }
.vcl-groupbox > legend {
  height: var(--tp-gb-cap);
  line-height: var(--tp-gb-cap);
}
.vcl-groupbox > [data-tp-align="alNone"] { margin-top: calc(1px - var(--tp-gb-cap)); }

/* TLabel */
.vcl-label {
  background: transparent;
  font: 11px Tahoma, sans-serif;
  color: var(--tp-fg, #1a1a1a);   /* v305: theme token */
}

/* v751: A VCL LABEL IS ONE LINE. TLabel's manifest declares WordWrap with a
   default of False and nothing consumed it, so the browser wrapped every caption
   that was a pixel too wide for its designed box -- measured on Export to Web,
   where four captions broke in two and pushed their rows apart. overflow is
   VISIBLE rather than hidden: an AutoSize label in the VCL is as wide as its
   text, so spilling is closer to the truth than clipping. The .dfm opts back in
   per instance via data-tp-wordwrap (componentRegistryUnit emits it). */
.vcl-label { white-space: nowrap; overflow: visible; }
.vcl-label[data-tp-wordwrap="1"] { white-space: normal; }

/* TListBox */
/* TListBox — a single-select scrollable list. `size="5"` in the template forces multi-row rendering (a plain
   <select> with size<=1 collapses to a dropdown); the geometry {Style} height then clips/scrolls it to the
   dropped size, same as any other comp. MultiSelect (the `multiple` attribute) is a later version. */
.vcl-listbox {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 13px Tahoma, "Segoe UI", sans-serif;   /* fallback — {Style} FontCss overrides when the model sets font */
  color: #000;
  padding: 0;
  overflow-y: auto;
}

/* TMainMenu */
.vcl-mainmenu-bar {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 20px;
  display: flex;
  align-items: stretch;
  box-sizing: border-box;
  background: #ffffff;
  border-bottom: 1px solid #f0f0f0;
  color: #000000;
  font: 12px "Segoe UI", Tahoma, sans-serif;
  white-space: nowrap;
  overflow: hidden;
  user-select: none;
}
.vcl-mainmenu-item {
  padding: 0 7px;
  line-height: 19px;
}
.vcl-mainmenu-item.is-off {
  color: #6d6d6d;
}

/* TMemo */
.vcl-memo {
  background: var(--tp-input-bg, #fff);   /* v376: theme token; a real memo's inline BevelCss (--tp-def-tmemo-*) still overrides bg/border */
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  padding: 1px 2px;
  resize: none;
  overflow: auto;
  white-space: pre-wrap;
}

/* TMenuItem */
/* TMenuItem -- a menu item as a DESIGN-TIME object.
   The item is nested inside its TMainMenu/TPopupMenu/parent TMenuItem in the
   .dfm, so {{Children}} is the SUBMENU: nesting in the designer is nesting in
   the menu. A separator is Caption = '-' (Delphi's own convention, and what
   Vcl.Menus.TMenuItem.IsLine reads). */

.vcl-menuitem {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 2px 8px;
  font: 12px/16px Tahoma, "Segoe UI", sans-serif;
  color: #1a1a1a;
  background: #fff;
  white-space: nowrap;
}

.vcl-menuitem-cap {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vcl-menuitem-shortcut {
  flex: 0 0 auto;
  color: #6b6b6b;
}

/* A submenu is laid out under its parent in the designer so the structure is
   visible while editing; the runtime menu popper positions it for real. */
.vcl-menuitem-sub {
  display: block;
}

.vcl-menuitem[data-tp-checked="1"] .vcl-menuitem-cap::before {
  content: "\2713\00a0";
}

.vcl-menuitem[data-tp-enabled="0"] {
  color: #9a9a9a;
}

/* Caption '-' -- the separator. Drawn as the rule it is, not as text. */
.vcl-menuitem[data-tp-separator="1"] {
  padding: 0;
  height: 7px;
}

.vcl-menuitem[data-tp-separator="1"] .vcl-menuitem-cap {
  border-top: 1px solid #d0d0d0;
  height: 0;
  margin: 3px 2px;
  font-size: 0;
}

/* TPanel */
/* TPanel — caption centered behind any children. v102: the static raised-bevel hack
   (.vcl-panel--outer-raised) is GONE; BevelCssFromDfm now emits background/border/box-shadow
   inline from the comp's Color/BorderStyle/Bevel* props (a fresh panel still gets its defaults). */
.vcl-panel {
  background: var(--tp-surface-2, #f0f0f0);   /* v376: theme token; a real panel's inline BevelCss (--tp-def-tpanel-bg) still overrides bg */
  box-sizing: border-box;
}
.vcl-panel-cap {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: inherit;
}

/* TPropertyGrid */
/* TPropertyGrid -- the object inspector's grid, as a catalog component.

   v592 (P1 criterion (b), THE MEASURED CLONE LOOK): every number in here is
   now a VARIABLE whose default IS the constant propGridUnit paints with, and
   the IDE seat overwrites them from inspector_metrics -- which reads those
   same constants out of the running grid. So the two renderers cannot drift
   by someone typing 11 where the painter used 16. The five that HAD drifted,
   found by measuring rather than by looking:
     indent   11 -> 16 (CGroupChildIndent)
     checkbox 12 -> 14 (CCheckBoxSize)
     drop     15 -> 17 (CDropBtnW), and it shows on the CURRENT ROW ONLY
     swatch   11x11 -> 16x8 ("wider + shorter than the checkbox so the two
                              don't look alike" -- the VCL comment, kept)
     the selection band across the WHOLE row -> the NAME CELL only, with a
     1px line above and below the value cell (DrawRowAt does exactly that)
   And the divider is ONE continuous line at x = nameW (Paint draws a single
   goVertLine and no horizontal lines), not a per-row border that only exists
   where a row happens to be painted. */

.vcl-pgrid {
  /* the painter's own numbers -- the seat pushes the live ones over these */
  --pg-rowh: 18px;
  --pg-namew: 99px;      /* CDefaultNameWidth */
  --pg-indent: 16px;     /* CGroupChildIndent */
  --pg-glyph: 14px;      /* CGroupGlyphWidth */
  --pg-gutter: 10px;     /* CValueGutter */
  --pg-check: 14px;      /* CCheckBoxSize */
  --pg-drop: 17px;       /* CDropBtnW */
  --pg-ell: 18px;        /* CEllipsisW */
  --pg-inset: 4px;       /* DrawRowAt's themed name inset */
  --pg-swatch-w: 16px;
  --pg-swatch-h: 8px;
  --pg-swatch-gap: 5px;
  --pg-bg: #ffffff;
  --pg-text: #000000;
  --pg-ro: #808080;
  --pg-sel: #cce4ff;
  --pg-link: #008000;
  --pg-line: #d8d8d8;

  position: relative;
  overflow: hidden;
  font: 12px/1.35 "Segoe UI", Tahoma, sans-serif;
  background: var(--pg-bg);
  color: var(--pg-text);
  outline: none;
  user-select: none;
}
.vcl-pgrid-vp { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; }
/* *** THE SCROLLBAR IS THE THEME'S, NOT THE GRID'S. *** (user, 2026-08-30:
   "in light mode, the inspector scrollbar stays dark, and it has a different
   style than the palette scrollbar") -- both halves of that had one cause.
   webThemeUnit already styles *::-webkit-scrollbar for EVERY page from
   --tp-sb-*, and it does so live on a theme toggle. This block predated that
   rule and overrode it with its own 16px rounded bar fed by --pg-sb-*, which
   client.js wrote INLINE from the grid's model -- a model that arrives when the
   grid loads and NOT when the theme changes. So the inspector kept the colours
   it was born with (dark, on a now-light IDE) and wore a different shape while
   doing it. Deleting the override fixes the colour AND the shape, and removes
   the whole staleness class rather than re-syncing it.
     The 2026-08-21 request this replaces ("scrollbars of prop lists also have
   dark mode") is still met -- by the global rule, for every panel at once. */

/* VIRTUAL ROWS: the spacer carries the FULL height so the scrollbar is
   honest, and only the visible window is in the DOM. P1's criterion (d) is a
   heavy form scrolling without lag, and that is decided here, not later. */
.vcl-pgrid-spacer { width: 1px; }
.vcl-pgrid-rows { position: absolute; left: 0; right: 0; top: 0; }

.vcl-pgrid-row { position: absolute; left: 0; right: 0; display: flex; align-items: center; box-sizing: border-box; }
/* the band is the NAME cell's, not the row's (DrawRowAt), and the value cell
   is framed top and bottom in the same colour */
.vcl-pgrid-row.is-sel .vcl-pgrid-name { background: var(--pg-sel); }
.vcl-pgrid-row.is-sel .vcl-pgrid-val  { box-shadow: inset 0 1px 0 var(--pg-sel), inset 0 -1px 0 var(--pg-sel); }
/* v1016: the name cell keeps its width -- a long value (a wrapped label's Caption) shrank it to 46 px
   and the value started over the name, cutting the "n" of Caption */
.vcl-pgrid-name { flex: 0 0 auto; box-sizing: border-box; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; height: 100%; display: flex; align-items: center; }
.vcl-pgrid-val  { box-sizing: border-box; padding-left: var(--pg-gutter); flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; height: 100%; display: flex; align-items: center; }
.vcl-pgrid-row.is-ro .vcl-pgrid-val { color: var(--pg-ro); }
.vcl-pgrid-row.is-link .vcl-pgrid-name { color: var(--pg-link); }

/* the group caret: a triangle, rotated when open -- the VCL grid drew two
   bitmaps (CaretSideBmp / CaretDownBmp) into a zone CGroupGlyphWidth wide,
   with the row's text starting immediately after that zone.

   *** AND IT NEEDS A HEIGHT OF ITS OWN. *** (user, 2026-08-21: "i dont see the
   caret in obj insp, and group props dont expand on click".) MEASURED: the box
   was 14x0. The seat overrides this rule with the IDE's REAL caret bitmaps --
   content:"" plus a background image sized width:100%/height:100% -- and a span
   whose only content is an empty pseudo-element has no content height for that
   100% to resolve against. The glyph sized the box and the box sized the glyph.
   A caret 0 px high is invisible AND unclickable, which is why one report named
   two bugs.
     align-self: stretch takes the height from the ROW instead, which is where
   DrawCaret takes it from; centring is the flexbox spelling of DrawCaret's own
   arithmetic, and it holds for both the bitmaps and the Unicode fallback. */
.vcl-pgrid-caret { width: var(--pg-glyph); flex: 0 0 var(--pg-glyph); align-self: stretch;
                   display: flex; align-items: center; justify-content: center;
                   cursor: pointer; color: var(--pg-ro); }
.vcl-pgrid-caret::before { content: '\25B6'; font-size: 8px; }
.vcl-pgrid-row.is-open > .vcl-pgrid-name > .vcl-pgrid-caret::before { content: '\25BC'; }

.vcl-pgrid-swatch { width: var(--pg-swatch-w); height: var(--pg-swatch-h); flex: 0 0 var(--pg-swatch-w); border: 1px solid #808080; box-sizing: border-box; margin-right: var(--pg-swatch-gap); }
.vcl-pgrid-check { width: var(--pg-check); height: var(--pg-check); flex: 0 0 var(--pg-check); border: 1px solid var(--tp-chk-line, #333); background: var(--tp-chk-bg, #fff); position: relative; box-sizing: border-box; }
.vcl-pgrid-check.is-on::after { content: ''; position: absolute; inset: -1px; background: var(--tp-chk-fg, #333); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13'%3E%3Cpath d='M2 6.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 13'%3E%3Cpath d='M2 6.5l3 3 6-6' fill='none' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat; }
/* DrawCheckBox writes True/False six pixels after the box, in the row's text
   colour (read-only rows in the read-only colour, which .is-ro already does) */
.vcl-pgrid-cbl { margin-left: 6px; }
/* the drop arrow and the "..." button appear on the CURRENT ROW ONLY -- the
   VCL grid paints both only when VisIdx = FRowIndex */
.vcl-pgrid-drop, .vcl-pgrid-ell { display: none; margin-left: auto; text-align: center; border-left: 1px solid var(--pg-line); cursor: default; box-sizing: border-box; }
.vcl-pgrid-row.is-sel .vcl-pgrid-drop { display: block; width: var(--pg-drop); flex: 0 0 var(--pg-drop); }
.vcl-pgrid-row.is-sel .vcl-pgrid-ell  { display: block; width: var(--pg-ell);  flex: 0 0 var(--pg-ell); }
.vcl-pgrid-drop::before { content: '\25BC'; font-size: 8px; }
.vcl-pgrid-ell::before { content: '\2026'; }

/* the pick list (v591): a borderless popup under the row's value cell -- the
   VCL grid's TPropDropForm in the web's own medium. The CONTENTS are still the
   host's; only the list is the component's. */
.vcl-pgrid-pop { position: absolute; z-index: 5; max-height: 160px; overflow-y: auto; background: var(--pg-bg); border: 1px solid #7a7a7a; box-shadow: 0 2px 6px rgba(0,0,0,.25); }
.vcl-pgrid-pick { padding: 1px 4px; white-space: nowrap; cursor: default; }
/* v743: a swatch inside a PICK. The row's swatch lives in a flex line, so it
   is sized with flex-basis; a pick is plain text, and width/height do not
   apply to an inline span -- the swatch would collapse to nothing. Scoped to
   picks so the row's own swatch keeps painting exactly as it does. */
.vcl-pgrid-pick > .vcl-pgrid-swatch { display: inline-block; vertical-align: middle; }
.vcl-pgrid-pick:hover { background: #e5f0fb; }
.vcl-pgrid-pick.is-sel { background: var(--pg-sel); }

/* the inline editor: one element, moved to the current row, exactly as the
   VCL grid kept ONE TEdit and repositioned it (SyncEditor) */
/* *** THE HEAVY RING WAS THE BROWSER'S, NOT OURS. *** (user, 2026-08-30:
   "inspector editable field gets too strong an outline, make it thinner".) The
   border here was already 1px; what showed was Chromium's focus ring, which an
   <input> gets for free and which is several pixels of high-contrast colour
   around a control that is 18px tall. Turning it off would leave focus
   invisible, so the 1px border CARRIES the focus instead -- and it follows the
   theme now rather than a hardcoded grey that was too dark on a light IDE. */
.vcl-pgrid-edit { position: absolute; box-sizing: border-box; padding: 0 3px; font: inherit;
                  border: 1px solid var(--pg-line, #7a7a7a); }
.vcl-pgrid-edit:focus { outline: none; border-color: var(--pg-sel-line, #3670c8); }
/* the column divider: ONE line, full height, at x = nameW -- Paint's single
   goVertLine. The 5px element around it is the drag zone (CColResizeTol). */
.vcl-pgrid-split { position: absolute; top: 0; bottom: 0; width: 5px; cursor: col-resize; }
.vcl-pgrid-split::before { content: ''; position: absolute; left: 2px; top: 0; bottom: 0; width: 1px; background: var(--pg-line); }

/* TRadioButton */
/* TRadioButton — same flex row as the checkbox (from the reference classic.css). */
.vcl-radio {
  display: flex;
  align-items: center;
  gap: 6px;
}
.vcl-radio > input {
  margin: 0;
  font: inherit;
  width: 1em !important;
  height: 1em !important;
  flex: 0 0 auto;
}

/* v751: same rule as TCheckBox -- a radio caption does not wrap. */
.vcl-radiobutton { white-space: nowrap; }
/* v1047: Alignment = taLeftJustify -- the caption on the left, the button at the right edge, as the VCL draws it */
.vcl-radio[data-tp-lefttext] { flex-direction: row-reverse; justify-content: space-between; }

/* TRadioGroup */
/* TRadioGroup — bordered group of radio options (Items.Strings), one checked via ItemIndex. Interactive sibling
   of TDBRadioGroup's display-only vcl-dbradiogroup (that one is pointer-events:none — a live bound field renders
   it, per the db-parity-convergence memory; this one has no DataSource, so clicking a radio is the only way to
   change ItemIndex, same as real Delphi). */
.vcl-radiogroup {
  box-sizing: border-box;
  border: 1px solid #a0a0a0;
  border-radius: 3px;
  margin: 0;
  padding: 2px 8px 6px;
  background: transparent;
  font: 13px Tahoma, "Segoe UI", sans-serif;
  color: #000;
  overflow: auto;
}
.vcl-radiogroup > legend {
  padding: 0 4px;
  font-weight: 600;
}
.vcl-radiogroup-items {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-top: 2px;
}
.vcl-radiogroup-item {
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  cursor: pointer;
}

/* v884 (sqlconvert MERGE 44): *** A LEGEND IS IN THE FLOW AND A VCL CAPTION IS
   NOT. *** A fieldset's content box starts BELOW its legend, so a caption too
   long for the box wraps and pushes everything down. MEASURED on a 200px group
   with a 61-character caption: the legend went 16px -> 48px (three lines) and
   the first radio moved 32px down; sqlconvert measured 44px on their own form
   and the third radio out of the box. A VCL group-box caption sits on the
   border, is CLIPPED when it does not fit, and moves nothing.
     nowrap + overflow:hidden is the whole fix, and it is deliberately not
   absolute positioning: the content box still starts below the caption, which
   is what the VCL does too. Only the WRAPPING was wrong. A caption that fits
   is unaffected, which is why the golden corpus does not move. */
.vcl-radiogroup > legend {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
  max-width: 100%;
  box-sizing: border-box;
}

/* v884 (sqlconvert MERGE 44): Columns. The VCL fills a multi-column radio group
   COLUMN-MAJOR -- six items in two columns are 1,2,3 then 4,5,6 -- which is
   exactly what CSS multicol does, so the layout is one property and no maths.
   The wrapper only exists when Columns > 1, so a one-column group emits the
   same bytes it always did. */
.vcl-radiogroup-cols { column-gap: 12px; }
.vcl-radiogroup-cols > .vcl-radiogroup-item { break-inside: avoid; }

/* TScrollBar */
/* TScrollBar (v740). A SCROLL BAR IS DRAWN BY THE PLATFORM, so there is no native HTML element to
   borrow the way TTrackBar borrows <input type=range> -- a range control has a different shape, a
   different hit behaviour and a different meaning. This paints the CLASSIC WIN32 GEOMETRY instead:
   two square end buttons whose side is the bar's short edge, a track between them, and a thumb.
   The layout flips on data-kind, which is the DFM's own Kind value substituted by the template.
     NOTHING HERE SCROLLS ANYTHING, and that is this catalog's arrangement rather than a gap in this
   control: the page draws, the component holds the state. */
.vcl-scrollbar {
  box-sizing: border-box;
  display: flex;
  background: var(--tp-sb-track, #f0f0f0);
  border: 1px solid var(--tp-sb-hover, #a6a6a6);
  overflow: hidden;
}
.vcl-scrollbar[data-kind="sbHorizontal"] { flex-direction: row; }
.vcl-scrollbar[data-kind="sbVertical"]   { flex-direction: column; }
.vcl-scrollbar > .btn {
  flex: 0 0 auto;
  background: var(--tp-sb-thumb, #cdcdcd);
}
.vcl-scrollbar[data-kind="sbHorizontal"] > .btn { width: 15px; height: 100%; }
.vcl-scrollbar[data-kind="sbVertical"]   > .btn { height: 15px; width: 100%; }
.vcl-scrollbar > .track { flex: 1 1 auto; position: relative; }
.vcl-scrollbar > .track > .thumb { background: var(--tp-sb-thumb, #cdcdcd); display: block; }
.vcl-scrollbar[data-kind="sbHorizontal"] > .track > .thumb { width: 30%; height: 100%; }
.vcl-scrollbar[data-kind="sbVertical"]   > .track > .thumb { height: 30%; width: 100%; }

/* TDateTimePicker */
.vcl-datetimepicker {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 11px Tahoma, sans-serif;
}

/* TListView */
/* v608: THEMEABLE, WITHOUT CHANGING WHAT IT LOOKS LIKE. Every colour is now
   a variable with ITS OWN OLD VALUE as the fallback, so a user's program that
   ships this component alone renders byte-for-byte as before, while a host that
   defines the variables (the IDE serves them at /ide-theme.css) gets a control
   in its own theme. Same move the property grid made in v590. */
.vcl-listview {
  box-sizing: border-box;
  border: 1px solid var(--tp-ro, #7a7a7a);
  background: var(--tp-bg, #fff);
  font: 11px Tahoma, sans-serif;
  overflow: auto;
}
.vcl-listview-hdr {
  display: flex;
  background: var(--tp-bar, #f0f0f0);
  border-bottom: 1px solid var(--tp-line, #d0d0d0);
  position: sticky;
  top: 0;
}
.vcl-listview-hdr > span {
  flex: 1;
  padding: 2px 6px;
  border-right: 1px solid var(--tp-line, #d0d0d0);
  font-weight: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vcl-listview-row {
  display: flex;
  cursor: default;
}
.vcl-listview-row > span {
  flex: 1;
  padding: 1px 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* v225: gridlines + row selection (built by the package client.js at runtime) */
.vcl-listview-gridlines .vcl-listview-row > span { border-right: 1px solid var(--tp-line, #e4e4e4); border-bottom: 1px solid var(--tp-line, #e4e4e4); }
.vcl-listview-row:hover { background: var(--tp-hover, #f2f7fd); }
.vcl-listview-row.vcl-listview-sel { background: var(--tp-sel, #cbe4fb); }
.vcl-listview-row.vcl-listview-sel > span { color: var(--tp-text, #000); }

/* TMonthCalendar */
/* TMonthCalendar (sqlconvert w019) -- a month at a glance. The look is UniGUI's
   TUniCalendar in the reference (an Ext.js date picker): a blue title bar with
   the arrows at its ends, bold weekday initials, a 7-wide grid, the selected day
   framed, other months' days greyed, and a Today button under it. Colours are
   the calendar family's own (#99BCE8 lines, #DAE5F3 chrome). */
.vcl-monthcalendar {
  box-sizing: border-box; display: flex; flex-direction: column;
  background: #fff; border: 1px solid #99BCE8;
  font: 11px Tahoma, "Segoe UI", sans-serif; color: #223; user-select: none;
  overflow: hidden;
}
.vcl-monthcalendar .vcl-mc-head {
  display: flex; align-items: center; height: 22px; flex: 0 0 22px;
  background: linear-gradient(to bottom, #DAE5F3, #C5D6EC); border-bottom: 1px solid #99BCE8;
}
.vcl-monthcalendar .vcl-mc-title { flex: 1 1 auto; text-align: center; font-weight: bold; color: #15428B; }
.vcl-monthcalendar .vcl-mc-nav {
  flex: 0 0 22px; width: 22px; height: 22px; padding: 0; border: 0; background: transparent;
  color: #15428B; font: bold 16px/22px Tahoma, sans-serif; cursor: pointer;
}
.vcl-monthcalendar .vcl-mc-nav:hover { background: rgba(255, 255, 255, 0.5); }
.vcl-monthcalendar .vcl-mc-days {
  display: grid; grid-template-columns: repeat(7, 1fr); flex: 0 0 18px;
  background: #EFF3FA; border-bottom: 1px solid #C5D6EC; font-weight: bold; color: #3C408C;
}
.vcl-monthcalendar .vcl-mc-days span { text-align: center; line-height: 18px; }
.vcl-monthcalendar .vcl-mc-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); grid-auto-rows: 1fr; flex: 1 1 auto; padding: 1px;
}
.vcl-monthcalendar .vcl-mc-day {
  border: 1px solid transparent; background: transparent; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer; border-radius: 2px; min-height: 18px;
}
.vcl-monthcalendar .vcl-mc-day:hover { background: #E6EEF9; }
.vcl-monthcalendar .vcl-mc-day:focus-visible { outline: 1px dotted #15428B; outline-offset: -2px; }
.vcl-monthcalendar .vcl-mc-other { color: #9AA3B5; }
.vcl-monthcalendar .vcl-mc-today { font-weight: bold; color: #B00; }
.vcl-monthcalendar .vcl-mc-sel { background: #DCE9F8; border-color: #7FA6D8; font-weight: bold; }
.vcl-monthcalendar .vcl-mc-foot {
  flex: 0 0 24px; display: flex; align-items: center; justify-content: center;
  border-top: 1px solid #C5D6EC; background: #EFF3FA;
}
.vcl-monthcalendar .vcl-mc-today-btn {
  font: inherit; padding: 1px 10px; cursor: pointer;
  border: 1px solid #99BCE8; border-radius: 2px; background: linear-gradient(to bottom, #fff, #E6EEF9); color: #15428B;
}
.vcl-monthcalendar .vcl-mc-today-btn:hover { background: #fff; }

/* TPageControl */
.vcl-pagecontrol {
  box-sizing: border-box;
  border: 1px solid #a0a0a0;
  background: #fff;
  padding: 2px;
}

/* The tab strip + page framing are built by the package client.js at runtime (design view stays inert).
   Cloned from tswebide2's TPageControl visual (the reference look). */
.vcl-pc-tabbar {
  position: absolute;
  left: 0; top: 0; right: 0;
  height: 26px;
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 2px;
  padding: 0 4px;
  box-sizing: border-box;
  border-bottom: 2px solid #0078d7;
  font: 11px Tahoma, sans-serif;
  background: #f0f0f0;
  z-index: 2;
}
.vcl-pc-tab {
  padding: 3px 12px;
  cursor: pointer;
  border: 1px solid #ccc;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background: #e8e8e8;
  color: #444;
  user-select: none;
  white-space: nowrap;
}
.vcl-pc-tab:hover:not(.vcl-pc-tab-active) { background: #dcdcdc; }
.vcl-pc-tab-active {
  background: #ffffff;
  border-color: #0078d7;
  color: #000;
  font-weight: bold;
  position: relative;
  bottom: -2px;
}

/* TProgressBar */
.vcl-progressbar {
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
  border: 1px solid #bcbcbc;
  background: #e6e6e6;
  vertical-align: top;
}
.vcl-progressbar::-webkit-progress-bar { background: #e6e6e6; }
.vcl-progressbar::-webkit-progress-value { background: #06b025; }
.vcl-progressbar::-moz-progress-bar { background: #06b025; }

/* IDE v1083: Orientation = pbVertical -- the bar stands up and fills from the bottom (Tools > Memory Calculator's
   RAM, flash and PSRAM gauges; any form's vertical bar). */
.vcl-progressbar[data-orient="pbVertical"] { writing-mode: vertical-lr; direction: rtl; }
/* ...and State, the VCL's TProgressBarState: pbsPaused amber, pbsError red */
.vcl-progressbar[data-state="pbsPaused"]::-webkit-progress-value { background: #e0a000; }
.vcl-progressbar[data-state="pbsPaused"]::-moz-progress-bar { background: #e0a000; }
.vcl-progressbar[data-state="pbsError"]::-webkit-progress-value { background: #d32f2f; }
.vcl-progressbar[data-state="pbsError"]::-moz-progress-bar { background: #d32f2f; }

/* TRichEdit */
.vcl-richedit {
  box-sizing: border-box;
  border: 1px solid #7a7a7a;
  background: #fff;
  font: 11px Tahoma, sans-serif;
  overflow: auto;
  padding: 2px;
  white-space: pre-wrap;   /* Lines.Strings: one line per row, as typed */
}

/* TStatusBar */
.vcl-statusbar {
  box-sizing: border-box;
  border-top: 1px solid #808080;
  background: #f0f0f0;
  display: flex;
  align-items: center;
  padding: 2px 6px;
  font: 11px Tahoma, sans-serif;
  color: #000;
}

/* sqlconvert w022: THE PANELS. One sunken cell per Panels.Strings line, laid
   out along the bar the way ComCtrls lays them out -- a fixed width where the
   line gives one, and the rest shared by the ones that do not. The flex basis
   is written inline by client.js because the width is per panel and comes from
   the .dfm. */
.vcl-statusbar-panel {
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  padding: 1px 6px;
  margin-right: 2px;
  border: 1px solid;
  border-color: #a0a0a0 #ffffff #ffffff #a0a0a0;   /* sunken, as the VCL draws it */
}
.vcl-statusbar-panel:last-child { margin-right: 0; }

/* TTabControl */
.vcl-tabcontrol {
  box-sizing: border-box;
  border: 1px solid #a0a0a0;
  background: #fff;
  padding: 2px;
}

/* The tab strip is built by the package client.js at runtime (design view stays inert). Cloned from
   tswebide2's TPageControl look; single shared client area (no per-tab pages). */
.vcl-tc-tabbar {
  position: absolute;
  left: 0; top: 0; right: 0;
  height: 26px;
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 2px;
  padding: 0 4px;
  box-sizing: border-box;
  border-bottom: 2px solid #0078d7;
  font: 11px Tahoma, sans-serif;
  background: #f0f0f0;
  z-index: 2;
}
.vcl-tc-tab {
  padding: 3px 12px;
  cursor: pointer;
  border: 1px solid #ccc;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background: #e8e8e8;
  color: #444;
  user-select: none;
  white-space: nowrap;
}
.vcl-tc-tab:hover:not(.vcl-tc-tab-active) { background: #dcdcdc; }
.vcl-tc-tab-active {
  background: #ffffff;
  border-color: #0078d7;
  color: #000;
  font-weight: bold;
  position: relative;
  bottom: -2px;
}

/* TTabSheet */
.vcl-tabsheet {
  box-sizing: border-box;
  background: #fff;
}

/* TToolBar */
.vcl-toolbar {
  box-sizing: border-box;
  border-bottom: 1px solid #a0a0a0;
  background: #f0f0f0;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
}

/* TToolButton */
.vcl-toolbutton {
  box-sizing: border-box;
  border: 1px solid transparent;
  background: transparent;
  font: 11px Tahoma, sans-serif;
  padding: 2px 8px;
  cursor: default;
}
.vcl-toolbutton:hover {
  border-color: #b0b0b0;
  background: #e8e8e8;
}

/* TTrackBar */
.vcl-trackbar {
  box-sizing: border-box;
  vertical-align: top;
}

/* TTreeView */
/* v608: THEMEABLE, WITHOUT CHANGING WHAT IT LOOKS LIKE. Every colour is now
   a variable with ITS OWN OLD VALUE as the fallback, so a user's program that
   ships this component alone renders byte-for-byte as before, while a host that
   defines the variables (the IDE serves them at /ide-theme.css) gets a control
   in its own theme. Same move the property grid made in v590. */
.vcl-treeview {
  box-sizing: border-box;
  border: 1px solid var(--tp-ro, #7a7a7a);
  background: var(--tp-bg, #fff);
  /* v629 (the user: "text is a bit too small"): the SIZE is the host's to set,
     and 11px stays the component's own -- so a user's program renders as it
     always did while the IDE, which knows what font it is wearing, asks for its
     own through /ide-theme.css. The same shape the colours took in v608. */
  font-family: var(--tp-font-family, Tahoma, sans-serif);
  font-size: var(--tp-font-size, 11px);
  overflow: auto;
  padding: 2px;
}
/* v629: the node's own image. v728: AT ITS NATURAL SIZE, CAPPED.
   The size used to arrive with the icon table, because the icon arrived as a
   data URI decoded out of a Win32 ImageList and a bitmap has no size of its own
   once it is base64. MEASURED when that table was retired: the component icons
   are 24x24 (130 files; one is 16) and the ui-icons the trees draw with are
   16x16 -- so the ImageLists' 24 and 16 were only ever restating the files.
   A max- rule draws each at what it is and still stops a stray large icon from
   pushing the row open. */
.vcl-tree-icon {
  flex: 0 0 auto;
  max-width: var(--tp-tree-icon-w, 24px);
  max-height: var(--tp-tree-icon-h, 24px);
  margin-right: 4px;
  image-rendering: -webkit-optimize-contrast;
}
/* user-select: none because a palette row is PRESSED AND DRAGGED. Without it a
   press starts a text selection, which is what canvas.js used to suppress with
   preventDefault on pointerdown -- and that also silenced mousedown, so the
   palette stopped arming and popups stopped closing. The narrow guard is here. */
.vcl-tree-node {
  user-select: none;
  -webkit-user-select: none;
  display: flex;
  align-items: center;
  padding: 1px 4px;
  white-space: nowrap;
  cursor: default;
}
.vcl-tree-node:hover { background: var(--tp-hover, #f2f7fd); }
.vcl-tree-node.vcl-tree-sel { background: var(--tp-sel, #cbe4fb); color: var(--tp-text, #000); }
.vcl-tree-caret {
  display: inline-block;
  width: 14px;
  /* *** v757: A DEFINITE HEIGHT, OR THE PICTURE HAS NO BOX. *** The user, on the
     new caret: "when click on caret only root is left, caret and subitems gone".
     The subitems were correctly hidden; the caret was MEASURED at 14x0. Its
     ::before carries the image with height:100%, and 100% of a parent whose own
     height is auto is zero -- with a text glyph the line box gave the span a
     height and hid the fault, and content:"" (which is what the image rule uses)
     takes it away. Stating the row height here resolves the percentage in every
     context, flex row or not, and does not fight CaretCss's own 100%. */
  height: 1.35em;
  text-align: center;
  cursor: pointer;
  color: var(--tp-ro, #555);
  flex: 0 0 auto;
  user-select: none;
}
/* *** v757 (the user: "wrong caret is used in the treeviews, should be same as
   in inspector ... the inspector caret is the one used on group props, before
   their name"). IT IS A PICTURE, NOT A GLYPH. *** The grid's group caret is
   ui-icons/caretside.png and caretdown.png -- webInspUnit.CaretCss paints them
   into .vcl-pgrid-caret::before, which is why reading the STYLESHEET found a
   triangle and the screen showed something else (measured: the grid's ::before
   content is "", i.e. an image).
     So the caret is drawn the same WAY here: a ::before carrying the Unicode
   triangle as the fallback, which CaretCss then overrides with the same two
   files it gives the grid. Standalone -- a catalog tree with no IDE serving
   /ui-icons -- the triangle stands, which is the grid's own fallback order. */
.vcl-tree-caret::before { content: '\25B6'; font-size: 8px; display: block;
                          width: 100%; height: 100%; line-height: 1.35em; }
.vcl-tree-caret.vcl-tree-open::before { content: '\25BC'; }
.vcl-tree-caret.vcl-tree-leaf::before { content: ''; }
.vcl-tree-caret.vcl-tree-leaf { cursor: default; }
.vcl-tree-label { flex: 1 1 auto; }
/* legacy static-mockup class (design view); runtime rebuilds innerHTML */
.vcl-tree-sub { padding-left: 20px; }

/* *** v728: THE PLACEHOLDER TILE, DRAWN HERE INSTEAD OF FETCHED. ***
   A component that ships no icons\icon.png gets a fallback tile, and until now
   the only way a page could show it was to ask the host for the ImageList's
   contents -- which needs a Win32 image list to exist and webIconUnit to decode
   it back into a PNG data URI. For a picture that is a dashed plate and a small
   box.
   THE COLOURS ARE THE HOST'S OWN, read off paletteDrawUnit.DrawPaletteTile
   rather than picked to look similar: plate, dashed border, glyph box and its
   outline, light and dark. The PROPORTION is its too -- the glyph is inset by a
   THIRD on every side, Rect(sz/3, sz/3, sz-sz/3, sz-sz/3), which is 33.3% here
   and needs no pixel size to be right.
   Six numbers stated in two places while the host and the page both still draw
   this. If DrawPaletteTile's colours move, these move with them. */
/* v728: 24px, because the ONLY tree that draws a placeholder is the palette (a
   component shipping no icon.png) and every component icon beside it is 24. */
.vcl-tree-icon-ph {
  flex: 0 0 auto;
  width: var(--tp-tree-icon-w, 24px);
  height: var(--tp-tree-icon-w, 24px);
  margin-right: 4px;
  box-sizing: border-box;
  position: relative;
  background: rgb(228, 231, 236);
  border: 1px dashed rgb(150, 156, 165);
}
.vcl-tree-icon-ph::after {
  content: '';
  position: absolute;
  left: 33.3%; top: 33.3%; right: 33.3%; bottom: 33.3%;
  background: rgb(196, 201, 208);
  border: 1px solid rgb(120, 126, 136);
  box-sizing: border-box;
}
.vcl-tree-icon-ph.vcl-tree-icon-ph-dark {
  background: rgb(45, 48, 54);
  border-color: rgb(96, 102, 112);
}
.vcl-tree-icon-ph.vcl-tree-icon-ph-dark::after {
  background: rgb(70, 74, 82);
  border-color: rgb(120, 126, 136);
}

