﻿/* TBevel */
/* TBevel — a 3D divider/frame. It does NOT declare borderWidth, so SurfaceCss never touches it; the bevel border is
   entirely from 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-bevel { box-sizing: border-box; }

/* box — full border. lowered = sunken, raised = popped-out. */
.vcl-bevel-box.vcl-bevel-lowered   { border: 2px inset  #c0c0c0; }
.vcl-bevel-box.vcl-bevel-raised    { border: 2px outset #c0c0c0; }
/* frame — a double-bevelled groove/ridge. */
.vcl-bevel-frame.vcl-bevel-lowered { border: 2px groove #c0c0c0; }
.vcl-bevel-frame.vcl-bevel-raised  { border: 2px ridge  #c0c0c0; }

/* single-edge lines */
.vcl-bevel-topLine.vcl-bevel-lowered    { border-top: 2px groove #c0c0c0; }
.vcl-bevel-topLine.vcl-bevel-raised     { border-top: 2px ridge  #c0c0c0; }
.vcl-bevel-bottomLine.vcl-bevel-lowered { border-bottom: 2px groove #c0c0c0; }
.vcl-bevel-bottomLine.vcl-bevel-raised  { border-bottom: 2px ridge  #c0c0c0; }
.vcl-bevel-leftLine.vcl-bevel-lowered   { border-left: 2px groove #c0c0c0; }
.vcl-bevel-leftLine.vcl-bevel-raised    { border-left: 2px ridge  #c0c0c0; }
.vcl-bevel-rightLine.vcl-bevel-lowered  { border-right: 2px groove #c0c0c0; }
.vcl-bevel-rightLine.vcl-bevel-raised   { border-right: 2px ridge  #c0c0c0; }

/* TBitBtn */
.vcl-bitbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ececec;
  background-image: var(--tp-grad-button);
  border: 1px solid #aaa;
  border-radius: 3px;
  padding: 3px 8px;
  cursor: pointer;
  box-sizing: border-box;
  white-space: nowrap;
  user-select: none;
}
.vcl-bitbtn:hover   { filter: brightness(1.06); }
.vcl-bitbtn:active  { filter: brightness(0.94); }

/* glyph position variants */
.vcl-bitbtn-right  { flex-direction: row-reverse; }
.vcl-bitbtn-top    { flex-direction: column; }
.vcl-bitbtn-bottom { flex-direction: column-reverse; }

/* hide broken icon when glyphUrl is empty */
.vcl-bitbtn-icon[src=""] { display: none; }
.vcl-bitbtn-icon {
  width: 16px;
  height: 16px;
  object-fit: contain;
  flex-shrink: 0;
}

/* TColorBox */
.vcl-colorbox-wrap { display: block; box-sizing: border-box; }
.vcl-colorbox {
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ffffff;
  border: 1px solid #aaa;
  border-radius: 3px;
  padding: 1px 2px;
  cursor: pointer;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
}
.vcl-colorbox option {
  padding: 2px 4px;
  font-size: 11px;
}

/* TColorListBox */
.vcl-colorlistbox-wrap { display: block; box-sizing: border-box; }
.vcl-colorlistbox {
  display: block;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ffffff;
  border: 1px solid #aaa;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  overflow-y: auto;
}
.vcl-colorlistbox option {
  padding: 2px 4px;
  line-height: 18px;
  font-size: 11px;
}
.vcl-colorlistbox option:checked {
  background-color: #0078d7;
  color: #fff;
}

/* TFlexPanel */
.vcl-flexpanel {
  display: block;
  background-color: #F0F0F0;
  box-sizing: border-box;
  overflow: visible;
}

/* TFlowPanel */
/* TFlowPanel — a container that FLOWS its children (CSS flexbox with wrap). layout defaults to 'flex' + flexWrap
   'wrap', so dropped children flow left-to-right and wrap to the next line. The flex CSS comes from the inline
   {Style} (LayoutCssFromDfm); the fill/border from SurfaceCss; gap controls the spacing between children. */
.vcl-flowpanel { background: #f0f0f0; box-sizing: border-box; }

/* TGridPanel */
/* TGridPanel — a container that lays its children out on a CSS grid. layout defaults to 'grid' with
   gridTemplateColumns '1fr 1fr' (two equal columns); set gridTemplateColumns/Rows (e.g. 'repeat(3,1fr)') + gap to
   shape it. The grid CSS comes from the inline {Style} (LayoutCssFromDfm); fill/border from SurfaceCss. */
.vcl-gridpanel { background: #f0f0f0; box-sizing: border-box; }

/* TImage */
/* TImage — a surface whose CONTENT is its backgroundImage (use the "..." picker on the backgroundImage row to browse/
   embed a file, or type a url). backgroundColor is the backdrop behind/around the image; backgroundSize=contain fits
   the whole image, cover fills the box. All of it comes from the inline {Style} (SurfaceCss + BgImageCss). */
.vcl-image { box-sizing: border-box; background-repeat: no-repeat; background-position: center;
  box-shadow: var(--tp-shadow, 0 0 transparent);   /* v330: optional user shadow (--tp-shadow set inline when shadow=True) */ }

/* TMaskEdit */
.vcl-maskedit {
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ffffff;
  border: 1px solid #aaa;
  border-radius: 3px;
  padding: 1px 3px;
  color: #000;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  letter-spacing: 0.05em;
}
.vcl-maskedit:focus {
  outline: none;
  border-color: #4a90d9;
  box-shadow: 0 0 0 2px rgba(74,144,217,0.25);
}
.vcl-maskedit::placeholder {
  color: #bbb;
  letter-spacing: normal;
}
.vcl-maskedit[data-readonly="True"] {
  background-color: #f5f5f5;
  cursor: default;
}

/* TPaintBox */
.vcl-paintbox {
  display: block;
  background-color: #ffffff;
  border: 1px solid #aaaaaa;
  box-sizing: border-box;
  overflow: hidden;
  cursor: crosshair;
}

/* TShape */
/* TShape — a styled box. The fill (backgroundColor) + outline (borderWidth/Style/Color) come from the inline
   {Style} (SurfaceCss); the `shape` prop only picks the corner rounding via the class below. */
.vcl-shape { box-sizing: border-box; }
.vcl-shape-roundRect,
.vcl-shape-roundSquare { border-radius: 8px; }
.vcl-shape-ellipse,
.vcl-shape-circle      { border-radius: 50%; }
/* rectangle / square: square corners (no radius) */

/* TSpeedButton */
/* TSpeedButton — a FLAT button: transparent until hovered/pressed. Because it's in ClassStyledSurface (v044), the
   inline SurfaceCss emits backgroundColor/border ONLY when the user SETS them, so this flat look is the default. */
.vcl-speedbutton {
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 3px;
  background: transparent;
  color: #1a1a1a;
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-speedbutton:hover:not(:disabled)  { background: rgba(0, 0, 0, 0.06); border-color: #b8b8b8; }
.vcl-speedbutton:active:not(:disabled) { background: rgba(0, 0, 0, 0.12); }
.vcl-speedbutton:disabled              { cursor: not-allowed; opacity: 0.55; }

/* TSplitter */
.vcl-splitter {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #F0F0F0;
  box-sizing: border-box;
  user-select: none;
}
.vcl-splitter-handle {
  width: 2px;
  height: 20px;
  background: repeating-linear-gradient(
    to bottom,
    #999 0px, #999 2px,
    transparent 2px, transparent 5px
  );
  border-radius: 1px;
  pointer-events: none;
}

/* 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; }

/* TFileDrop */
/* TFileDrop — drop zone / click-to-pick. Dashed border + centred caption; client.js toggles
   data-tp-drag-hover during a drag-over for the highlight. Ported verbatim from the reference. */

.vcl-filedrop {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: #fafafa;
  border: 2px dashed rgb(176, 176, 176);
  color: #808080;
  font: 11px Tahoma, sans-serif;
  border-radius: 4px;
  padding: 4px;
  cursor: pointer;
  box-sizing: border-box;
  user-select: none;
}
.vcl-filedrop[data-tp-drag-hover="1"] {
  background: #eef4ff;
  border-color: #3b78e7;
  color: #3b78e7;
}
.vcl-filedrop-cap { pointer-events: none; }

/* TFooter */
/* TFooter — a footer bar of static text + clickable links. client.js builds the content from the Text +
   Links props; a no-URL link fires OnLinkClick, a URL link navigates. Modeled on the reference .vcl-footer. */

.vcl-footer {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 100%;
  padding: 0 16px;
  background: #f5f6f8;
  border-top: 1px solid #c2c7d0;
  font: 11px Tahoma, sans-serif;
  color: #333;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
}
.vcl-footer--center { justify-content: center; }
.vcl-footer--right { justify-content: flex-end; }
.vcl-footer--noborder { border-top: none; }

/* the static text sits first and pushes the links to the right (left-aligned default); hidden when empty */
.vcl-footer-text { flex: 0 0 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.vcl-footer-text:empty { display: none; }
.vcl-footer:not(.vcl-footer--center):not(.vcl-footer--right) .vcl-footer-text { margin-right: auto; }

.vcl-footer-link { color: #0066cc; text-decoration: none; white-space: nowrap; cursor: pointer; flex: 0 0 auto; }
.vcl-footer-link:hover { text-decoration: underline; }

/* 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 — verbatim from the reference styles/classic.css (.vcl-header*). client.js builds the bar
   DOM (brand/menu/search/buttons/login/burger), mounts the dropdown menus (inline-styled .vcl-popup-menu),
   and toggles the responsive collapse classes. Panels (search/login) + popups are appended to <body>. */

.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);   /* v305: theme tokens */
  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; }  /* v331: taller hover-highlight box (+3px top / +6px bottom) */
.vcl-header-mi:hover { background: var(--tp-hover, rgba(0, 0, 0, 0.06)); }
/* 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--invert-sel .vcl-header-topmenu .vcl-header-mi:not(.vcl-header-mi--disabled):hover {
  background: var(--hdr-top-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--has-topmenu { flex-direction: column; align-items: stretch; gap: 0; padding: 0; }
.vcl-header-topmenu {
  box-sizing: border-box; display: flex; align-items: center; gap: 2px;
  height: var(--hdr-top-h, 28px); min-height: var(--hdr-top-h, 28px);
  padding: 0 16px; border-bottom: 1px solid var(--tp-border, #888); overflow: hidden;
}
.vcl-header-topmenu--right { justify-content: flex-end; }
.vcl-header--topmenu-noborder .vcl-header-topmenu { border-bottom: none; }
.vcl-header-row2 {
  box-sizing: border-box; display: flex; align-items: center; gap: 12px;
  flex: 1 1 auto; min-height: var(--hdr-base-h, 64px); padding: 0 16px; min-width: 0;
}
.vcl-header--menu-left .vcl-header-row2 { gap: 0; }
.vcl-header--has-topmenu .vcl-header-logo { max-height: calc(var(--hdr-base-h, 64px) - 20px); }
.vcl-header--topmenu-autohide .vcl-header-topmenu {
  transition: height 0.18s ease, min-height 0.18s ease, opacity 0.18s ease, padding 0.18s ease;
}
.vcl-header--topmenu-hidden .vcl-header-topmenu {
  height: 0; min-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; border-bottom: 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%);
}
.vcl-header--collapsed:not(.vcl-header--has-topmenu) { justify-content: center; }
.vcl-header--collapsed:not(.vcl-header--has-topmenu):not(.vcl-header--sticky) { position: relative; }
.vcl-header--collapsed.vcl-header--has-topmenu .vcl-header-row2 { justify-content: center; position: relative; }
.vcl-header--collapsed .vcl-header-brand { margin-left: 0 !important; margin-right: 0 !important; }
.vcl-header--collapsed .vcl-header-topmenu { display: none; }
.vcl-header--collapsed.vcl-header--has-topmenu {
  height: var(--hdr-base-h, 64px) !important; min-height: var(--hdr-base-h, 64px) !important;
}

.vcl-header-search {
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  width: 32px; 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-search:hover { background: var(--tp-hover, rgba(0, 0, 0, 0.06)); }
.vcl-header-search svg { width: 18px; height: 18px; display: block; }
.vcl-header--menu-left:not(.vcl-header--has-btns) .vcl-header-search { margin-left: auto; }
.vcl-header--menu-left.vcl-header--has-btns .vcl-header-btns { margin-left: auto; }
.vcl-header--collapsed .vcl-header-search {
  position: absolute; right: calc(16px + var(--hdr-page-inset, 0px)); top: 50%;
  transform: translateY(-50%); align-self: auto; margin-right: 0;
}

.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--menu-tight .vcl-header-menu { gap: 0; }
.vcl-header--menu-tight .vcl-header-mi { padding: 3px 6px 6px; }
.vcl-header--btns-tight .vcl-header-btns { display: none; }
.vcl-header--menu-left.vcl-header--btns-tight .vcl-header-search { margin-left: auto; }

.vcl-header-search-panel {
  position: fixed; z-index: 9999; background: var(--tp-surface, #fff); border: 1px solid var(--tp-border, #c0c0c0);   /* v305: theme tokens */
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); border-radius: 4px; padding: 8px;
  display: flex; gap: 6px; align-items: center;
}
.vcl-header-search-panel input {
  font: 13px Tahoma, sans-serif; padding: 4px 8px; border: 1px solid var(--tp-input-border, #c0c0c0); border-radius: 3px;
  width: 200px; outline: none; color: var(--tp-fg, #222); background: var(--tp-input-bg, #fff);
}
.vcl-header-search-panel input:focus { border-color: #5b9dd9; }
.vcl-header-search-panel button {
  font: 13px Tahoma, sans-serif; padding: 4px 10px; 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-search-panel button:hover { background: var(--tp-hover, #e6e6e6); }

.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--has-login .vcl-header-search { margin-right: 0; }
.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--collapsed.vcl-header--has-login .vcl-header-search {
  right: calc(16px + var(--hdr-page-inset, 0px) + 40px);
}

.vcl-header-login-panel {
  position: fixed; z-index: 9999; background: var(--tp-surface, #fff); border: 1px solid var(--tp-border, #c0c0c0);   /* v305: theme tokens */
  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; }
/* v326: sign-in <-> create-account toggle link under the login panel */
.vcl-header-login-toggle { text-align: center; margin-top: 2px; }
/* v334: match the Sign in button (13px), not the tiny 11px it used to be. */
.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; }

/* v333: public @username + avatar. The header shows a small round avatar thumbnail in the account
   button when a user has one. (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;
}

/* 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; }

/* 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); }

/* THTMLText */
/* THTMLText — a read-only block of webpage-formatted text. client.js injects the HTML property as
   static innerHTML (embedded <script> stays inert). Ported verbatim from the reference .vcl-htmltext. */

.vcl-htmltext {
  background: #fff;
  border: 1px solid rgb(173, 173, 173);
  overflow: auto;
  padding: 6px 10px;
  box-sizing: border-box;
  line-height: 1.5;
  font: 14px/1.5 "Segoe UI", Tahoma, sans-serif;
  color: #1a1a1a;
  word-wrap: break-word;
}

/* TpAdminButton */
/* TpAdminButton (v301) — the DimWhenLocked appearance: visible but disabled + greyed, so users can
   SEE what an upgrade/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;
}
.vcl-graph-canvas { display: block; width: 100%; height: 100%; }

/* TpHTMLMemo */
/* TpHTMLMemo — a memo whose content is RAW HTML, injected so its <script>/<style>/<link> tags run as
   part of the document. client.js does the script-executing injection. Ported verbatim from the
   reference .vcl-htmlmemo. (Contrast .vcl-htmltext, which injects statically with scripts inert.) */

.vcl-htmlmemo {
  background: #fff;
  border: 1px solid rgb(173, 173, 173);
  overflow: auto;
  padding: 2px 4px;
  box-sizing: border-box;
}

/* 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;
}

/* TpTsClock */
.vcl-tsclock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 13px "Consolas", "Cascadia Mono", monospace;
  color: #28d07a;
  background: #0f1410;
  border: 1px solid #2a7;
  border-radius: 4px;
  padding: 0 8px;
  box-sizing: border-box;
  letter-spacing: 1px;
}

/* 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; }
.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; }
}

/* TSwitch */
/* v058: TSwitch — an IMAGE-BASED rotary switch (demo of the per-package img/ convention). The dial
   (switch-bg.png — a 100x100 "0/1" panel) is the element background; the knob (rotary-off.png) is an <img>
   centred on the dial seat and CSS-rotated to the ON angle. Both images are served from the package's img/
   folder via /comp-assets. Ported from the reference IDE's .vcl-switch rules (styles/classic.css).
   ON has TWO triggers: .tp-on (toggled at runtime by client.js on click) and [data-state="True"] (the SSR'd
   State prop — so the inert /design preview reflects the comp's State even with no client JS). */
.vcl-switch {
  position: relative;
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
  outline: none;
  background: url('/comp-assets/Custom/TSwitch/img/switch-bg.png') center / contain no-repeat;
}

/* the knob — centred on the dial seat (top 58%), 22% of the dial width; <img> keeps its native aspect. */
.vcl-switch-knob {
  position: absolute;
  left: 50%;
  top: 60%;
  width: 22%;
  height: 70%;
  transform: translate(-50%, -50%) rotate(0deg);
  transform-origin: 50% 50%;
  transition: transform 200ms ease;
  pointer-events: none;
}

/* ON — rotate the knob toward "1"; the small translate nudge re-seats the rotated knob on the dial
   (tuned for the 100px dial art, exactly as the reference). */
.vcl-switch.tp-on .vcl-switch-knob,
.vcl-switch[data-state="true" i] .vcl-switch-knob {   /* i = case-insensitive: matches true / True */
  transform: translate(calc(-50% - 5px), calc(-50% - 2px)) rotate(45deg);
}

/* TDataSource */
/* v049: TDataSource is a non-visual puck — it reuses the shared .vcl-nonvisual base (the dashed placeholder
   tile + centred name) defined in components/Data/TMySQLDatabase/styles.css, which loads alongside this file
   via /components.css. No per-comp styling needed. */

/* 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;
  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 { font-weight: bold; margin: 0 4px; min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vcl-dbcalendar-toolbar .tp-cal-spacer { flex: 1 1 auto; }
.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;
}
.vcl-cal .cal-break-area {
  position: absolute;
  background: repeating-linear-gradient(45deg, #f9f9f9, #f9f9f9 10px, #f2f2f2 10px, #f2f2f2 20px);
  opacity: 0.7; z-index: 1; display: flex; align-items: center; padding-left: 12px;
  color: #ff3333; font-weight: bold; pointer-events: none;
  border-top: 1px solid #ffcccc; border-bottom: 1px solid #ffcccc;
}
/* 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; }
/* 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 {
  background-image: repeating-linear-gradient(45deg, rgba(0,0,0,.06) 0 6px, transparent 6px 12px);
  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;
  font-size: 11px; 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) */
  position: absolute; font-size: 10px; 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: #FFFF77; border-color: #FF0000; box-shadow: inset 0 0 0 1px #FF0000; z-index: 5;
}
/* 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; }
.vcl-cal .cal-month-app {
  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;
  }
}

/* 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 (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; }

/* 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); } }
.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;
  height: 22px;
  line-height: 22px;
  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. */
.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) */
.vcl-dbgrid-cell > input[type="checkbox"] { 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); }
/* 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;
}
.vcl-dbnav-btn {
  flex: 1 1 0;
  min-width: 0;
  border: none;
  border-right: 1px solid #c8c8c8;
  background: linear-gradient(#fdfdfd, #e6e6e6);
  color: #222;
  cursor: pointer;
  padding: 0 6px;
  line-height: 1;
  font: inherit;
}
.vcl-dbnav-btn:last-child { border-right: none; }
.vcl-dbnav-btn:hover  { background: linear-gradient(#ffffff, #eaf2fb); }
.vcl-dbnav-btn:active { background: linear-gradient(#dcdcdc, #cfcfcf); }

/* 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: ""; }

/* 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. */

/* TMySQLQuery */
/* v048: TMySQLQuery uses the shared non-visual "puck" — all of its styling (the dashed placeholder
   tile + centred name) lives in components/Data/TMySQLDatabase/styles.css under .vcl-nonvisual, which
   loads alongside this file via /components.css. No per-comp icon: both data comps show the same default
   puck (the name label distinguishes them), matching the reference IDE's no-icon fallback. */

/* TButton */
/* v305 (css-import.txt M3/S4): colour literals now flow through the --tp-* theme tokens; every
   fallback is the exact pre-v305 literal, so with no token theme the look is pixel-identical. */
.vcl-button {
  cursor: pointer;
  border: 1px solid transparent;            /* transparent ring for the inset look */
  border-radius: 3px;
  background-color: var(--tp-btn-bg, #ececec);          /* v038: base — shows when backgroundGradient=none */
  background-image: var(--tp-grad-button);             /* v038: the project's button gradient token (classic.css) */
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px var(--tp-btn-ring, rgb(173, 173, 173)), var(--tp-shadow, 0 0 transparent);   /* v034: ring + optional user shadow */
  color: var(--tp-btn-fg, #1a1a1a);
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
}
.vcl-button:hover:not(:disabled) {
  background-image: var(--tp-grad-button-hover, linear-gradient(to bottom, #e8f0fe, #c2d4f8));   /* v038: longhand so a set backgroundColor still shows through where applicable */
  box-shadow: inset 0 0 0 1px var(--tp-accent, #4a7abf), var(--tp-shadow, 0 0 transparent);
}
.vcl-button:active:not(:disabled) {
  background-image: var(--tp-grad-button-active, linear-gradient(to bottom, #c2d4f8, #e8f0fe));
}
.vcl-button:disabled { cursor: not-allowed; opacity: 0.55; }

/* TCheckBox */
.vcl-checkbox {
  display: flex;
  align-items: center;
  gap: 4px;
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  white-space: nowrap;
  cursor: default;
  user-select: none;
}
.vcl-checkbox input { margin: 0; flex: none; }
/* caption fills the remaining width so the inherited textAlign (text-align) actually moves the text */
.vcl-checkbox > [data-tp-caption-host] { flex: 1; text-align: inherit; }

/* TComboBox */
/* TComboBox — a plain (non-DB) dropdown of its `items`. Unlike TDBComboBox this is a REAL, interactive select:
   the user picks an option and its value mirrors to the worker (this.Combo.value) on change, and the worker can
   set this.Combo.value to select an option. Options come from the {DataFieldOptions} token (which reads `items`;
   with no dataField it never resolves a DB field, so the control needs no database connection to render/work). */
.vcl-combobox {
  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-combobox-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;
  /* interactive — NOT pointer-events:none like the display-only TDBComboBox */
}
.vcl-combobox-select:disabled { color: #7a7a7a; background: #f0f0f0; }

/* TEdit */
/* v028: TEdit is a WRAPPER (positioned, carries data-tp-name + the inline {Style} border/bg/font) holding a
   transparent <input> fill + a validation-error message. The wrapper styles below are FALLBACKS — the inline
   {Style} (SurfaceCss border/background, FontCss font) overrides them when the model sets those props. */
.vcl-edit-field {
  background: var(--tp-input-bg, #fff);   /* v305: theme tokens, fallbacks = pre-v305 literals */
  border: 1px solid var(--tp-input-border, rgb(173, 173, 173));
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  box-shadow: var(--tp-shadow, 0 0 transparent);   /* v034: optional user shadow */
}
.vcl-edit-input {                 /* the real input: a transparent fill of the wrapper */
  width: 100%; height: 100%;
  border: 0; background: transparent; color: inherit; font: inherit; text-align: inherit;   /* inherit the wrapper's textAlign */
  outline: none; box-sizing: border-box; margin: 0; padding: 0 3px;
}
.vcl-edit-error {                 /* the validation message, just below the field (hidden unless invalid) */
  position: absolute; top: 100%; left: 0;
  font: 11px Tahoma, sans-serif; color: #c00;
  white-space: nowrap; display: none; pointer-events: none;
}
.vcl-edit-field[data-invalid="True"] { box-shadow: inset 0 0 0 1px #c00, var(--tp-shadow, 0 0 transparent); }   /* red ring (+ user shadow) */
.vcl-edit-field[data-invalid="True"] .vcl-edit-error { display: block; }

/* TGroupBox */
.vcl-groupbox {
  border: 1px solid var(--tp-border, #a0a0a0);   /* v305: theme token */
  border-radius: 0;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  min-inline-size: 0;   /* reset the UA fieldset min-width so geometry is exact */
  box-shadow: var(--tp-shadow, 0 0 transparent);   /* v034: optional user shadow */
}
.vcl-groupbox > legend {
  font: 11px Tahoma, sans-serif;
  color: var(--tp-fg, #000);   /* v305: theme token */
  margin-left: 6px;
  padding: 0 3px;
}

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

/* TMemo */
/* v028: TMemo is a WRAPPER (positioned, carries data-tp-name + the inline {Style} border/bg/font) holding a
   transparent <textarea> fill + a validation-error message. Wrapper styles are FALLBACKS — inline {Style} wins. */
.vcl-memo-field {
  background: var(--tp-input-bg, #fff);   /* v305: theme token, fallback = pre-v305 literal */
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  box-shadow: var(--tp-shadow, 0 0 transparent);   /* v034: optional user shadow */
}
.vcl-memo-input {                 /* the real textarea: a transparent fill of the wrapper */
  width: 100%; height: 100%;
  border: 0; background: transparent; color: inherit; font: inherit; text-align: inherit;   /* inherit the wrapper's textAlign */
  outline: none; box-sizing: border-box; margin: 0; padding: 1px 2px;
  resize: none; overflow: auto; white-space: pre-wrap;
}
.vcl-memo-error {                 /* the validation message, just below the field (hidden unless invalid) */
  position: absolute; top: 100%; left: 0;
  font: 11px Tahoma, sans-serif; color: #c00;
  white-space: nowrap; display: none; pointer-events: none;
}
.vcl-memo-field[data-invalid="True"] { box-shadow: inset 0 0 0 1px #c00, var(--tp-shadow, 0 0 transparent); }
.vcl-memo-field[data-invalid="True"] .vcl-memo-error { display: block; }

/* TPanel */
.vcl-panel {
  background: var(--tp-surface-2, #f0f0f0);   /* v305: theme token */        /* fallback; the inline SurfaceCss overrides background/border from props */
  box-sizing: border-box;
  box-shadow: var(--tp-shadow, 0 0 transparent);   /* v034: optional user shadow (--tp-shadow set inline when shadow=True) */
}
.vcl-panel-cap {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 11px Tahoma, sans-serif;
  color: var(--tp-fg, #000);   /* v305: theme token */
  pointer-events: none;   /* caption sits behind the panel's child controls */
}

/* TRadioButton */
.vcl-radio {
  display: flex;
  align-items: center;
  gap: 4px;
  font: 11px Tahoma, sans-serif;
  box-sizing: border-box;
  white-space: nowrap;
  cursor: default;
  user-select: none;
}
.vcl-radio input { margin: 0; flex: none; }
/* caption fills the remaining width so the inherited textAlign (text-align) actually moves the text */
.vcl-radio > [data-tp-caption-host] { flex: 1; text-align: inherit; }

/* TDateTimePicker */
.vcl-datetimepicker {
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ffffff;
  border: 1px solid #aaa;
  border-radius: 3px;
  padding: 1px 3px;
  color: #000;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  cursor: default;
}
.vcl-datetimepicker:focus {
  outline: none;
  border-color: #4a90d9;
  box-shadow: 0 0 0 2px rgba(74,144,217,0.25);
}

/* TListView */
.vcl-listview {
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
  border: 1px solid #aaa;
  box-sizing: border-box;
  overflow: auto;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  width: 100%;
  height: 100%;
}
.vcl-listview-header {
  display: flex;
  background-color: #f0f0f0;
  border-bottom: 1px solid #ccc;
  font-weight: bold;
  flex-shrink: 0;
}
.vcl-listview-row {
  display: flex;
  border-bottom: 1px solid #e8e8e8;
}
.vcl-listview-row:hover        { background-color: #e8f0fe; }
.vcl-listview-row.selected     { background-color: #cce0ff; }
.vcl-listview-cell {
  padding: 2px 6px;
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  border-right: 1px solid #e0e0e0;
}
.vcl-listview-cell:last-child  { border-right: none; }
.vcl-listview-header .vcl-listview-cell { border-right: 1px solid #ccc; }

/* TPageControl */
.vcl-pagecontrol {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
}
.vcl-pc-tabbar {
  display: flex;
  flex-direction: row;
  flex-shrink: 0;
  border-bottom: 2px solid #0078d7;
  gap: 2px;
  padding: 0 4px;
}
.vcl-pc-tab {
  padding: 4px 12px;
  cursor: pointer;
  border: 1px solid #ccc;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background-color: #e8e8e8;
  color: #444;
  font-size: 11px;
  user-select: none;
}
.vcl-pc-tab-active {
  background-color: #ffffff;
  border-color: #0078d7;
  color: #000;
  font-weight: bold;
  position: relative;
  bottom: -2px;
}
.vcl-pc-tab:hover:not(.vcl-pc-tab-active) { background-color: #d8d8d8; }
.vcl-pc-body {
  flex: 1;
  position: relative;
  overflow: hidden;
  border: 1px solid #0078d7;
  border-top: none;
  background-color: #ffffff;
  box-sizing: border-box;
}
/* bottom tabs variant */
.vcl-pc-tabs-bottom { flex-direction: column-reverse; }
.vcl-pc-tabs-bottom .vcl-pc-tabbar {
  border-top: 2px solid #0078d7;
  border-bottom: none;
}
.vcl-pc-tabs-bottom .vcl-pc-tab {
  border-bottom: none;
  border-top: none;
  border-radius: 0 0 4px 4px;
}
.vcl-pc-tabs-bottom .vcl-pc-tab-active { bottom: 0; top: -2px; }
.vcl-pc-tabs-bottom .vcl-pc-body {
  border: 1px solid #0078d7;
  border-bottom: none;
}

/* TProgressBar */
.vcl-progressbar {
  display: block;
  background-color: #e0e0e0;
  border: 1px solid #bbb;
  border-radius: 3px;
  box-sizing: border-box;
  overflow: hidden;
  position: relative;
}
.vcl-progressbar-fill {
  height: 100%;
  width: var(--tp-pb-pct, 0%);
  background-color: #0078d7;
  background-image: linear-gradient(to bottom, #4da3f7, #0055b0);
  border-radius: inherit;
  transition: width 0.3s ease;
  min-width: 0;
  max-width: 100%;
}
/* vertical variant: rotate the whole bar */
.vcl-pb-vertical {
  transform: rotate(-90deg);
  transform-origin: center center;
}

/* TRichEdit */
.vcl-richedit {
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  background-color: #ffffff;
  border: 1px solid #aaa;
  border-radius: 3px;
  padding: 4px;
  color: #000;
  box-sizing: border-box;
  overflow: auto;
  word-wrap: break-word;
  white-space: pre-wrap;
  cursor: text;
}
.vcl-richedit p {
  margin: 0 0 4px 0;
}
.vcl-richedit[data-readonly="True"] {
  background-color: #f5f5f5;
  cursor: default;
}

/* TStatusBar */
.vcl-statusbar {
  display: flex;
  align-items: center;
  background-color: #F0F0F0;
  border-top: 1px solid #ccc;
  box-sizing: border-box;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  padding: 0 4px;
  width: 100%;
}
.vcl-statusbar-panel {
  padding: 2px 4px;
  border-right: 1px solid #ccc;
  color: #000;
}

/* TTabControl */
.vcl-tabcontrol {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
}
.vcl-tc-tabbar {
  display: flex;
  flex-direction: row;
  flex-shrink: 0;
  border-bottom: 2px solid #888;
  gap: 2px;
  padding: 0 4px;
}
.vcl-tc-tab {
  padding: 4px 12px;
  cursor: pointer;
  border: 1px solid #ccc;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background-color: #e8e8e8;
  color: #444;
  font-size: 11px;
  user-select: none;
}
.vcl-tc-tab-active {
  background-color: #ffffff;
  border-color: #888;
  color: #000;
  font-weight: bold;
  position: relative;
  bottom: -2px;
}
.vcl-tc-tab:hover:not(.vcl-tc-tab-active) { background-color: #d8d8d8; }
.vcl-tc-body {
  flex: 1;
  position: relative;
  overflow: hidden;
  border: 1px solid #888;
  border-top: none;
  background-color: #ffffff;
  box-sizing: border-box;
}
.vcl-tc-tabs-bottom { flex-direction: column-reverse; }
.vcl-tc-tabs-bottom .vcl-tc-tabbar {
  border-top: 2px solid #888;
  border-bottom: none;
}
.vcl-tc-tabs-bottom .vcl-tc-tab {
  border-radius: 0 0 4px 4px;
}
.vcl-tc-tabs-bottom .vcl-tc-tab-active { bottom: 0; top: -2px; }
.vcl-tc-tabs-bottom .vcl-tc-body {
  border: 1px solid #888;
  border-bottom: none;
}

/* TTabSheet */
.vcl-tabsheet {
  display: block;
  background-color: #ffffff;
  box-sizing: border-box;
  padding: 4px;
}

/* TToolBar */
.vcl-toolbar {
  display: flex;
  flex-direction: row;
  align-items: center;
  background-color: #F0F0F0;
  border-bottom: 1px solid #ccc;
  box-sizing: border-box;
  gap: 2px;
  padding: 2px 4px;
}

/* TToolButton */
.vcl-toolbutton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background-color: transparent;
  border: 1px solid transparent;
  border-radius: 2px;
  padding: 2px 4px;
  cursor: pointer;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  user-select: none;
  box-sizing: border-box;
  min-width: 23px;
  height: 22px;
}
.vcl-toolbutton:hover  { background-color: rgba(0,0,0,0.07); border-color: rgba(0,0,0,0.15); }
.vcl-toolbutton:active { background-color: rgba(0,0,0,0.14); }

/* glyph position */
.vcl-toolbutton-glyph-right  { flex-direction: row-reverse; }
.vcl-toolbutton-glyph-top    { flex-direction: column; }
.vcl-toolbutton-glyph-bottom { flex-direction: column-reverse; }

/* separator style: a thin vertical line, not interactive */
.vcl-toolbutton-separator {
  width: 6px;
  min-width: 6px;
  padding: 0;
  border: none;
  cursor: default;
  background: transparent;
  position: relative;
}
.vcl-toolbutton-separator::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 10%;
  height: 80%;
  width: 1px;
  background-color: #bbb;
}
.vcl-toolbutton-separator:hover { background: transparent; border-color: transparent; }

/* hide empty icon */
.vcl-toolbutton-icon[src=""] { display: none; }
.vcl-toolbutton-icon { width: 16px; height: 16px; object-fit: contain; flex-shrink: 0; }

/* TTrackBar */
.vcl-trackbar {
  -webkit-appearance: slider-horizontal;
  appearance: auto;
  width: 100%;
  height: 100%;
  cursor: pointer;
  box-sizing: border-box;
  margin: 0;
  accent-color: #0078d7;
}
.vcl-trackbar-vertical {
  -webkit-appearance: slider-vertical;
  appearance: auto;
  writing-mode: vertical-lr;
  direction: rtl;
  width: 100%;
  height: 100%;
}

/* TTreeView */
.vcl-treeview {
  display: block;
  background-color: #ffffff;
  border: 1px solid #aaa;
  box-sizing: border-box;
  overflow: auto;
  font-family: Tahoma, sans-serif;
  font-size: 11px;
  padding: 2px;
}
.vcl-tree-node {
  display: flex;
  flex-direction: column;
  padding-left: 4px;
}
.vcl-tree-node > span {
  display: inline-flex;
  align-items: center;
  padding: 1px 2px;
  border-radius: 2px;
  cursor: pointer;
}
.vcl-tree-node > span:hover { background-color: #e8f0fe; }
.vcl-tree-toggle {
  display: inline-block;
  width: 14px;
  text-align: center;
  color: #555;
  font-size: 10px;
  flex-shrink: 0;
}
.vcl-tree-leaf { visibility: hidden; }
.vcl-tree-label {
  padding-left: 2px;
  white-space: nowrap;
}
.vcl-tree-children {
  display: none;
  padding-left: 14px;
}
.vcl-tree-open > .vcl-tree-children { display: flex; flex-direction: column; }

