/* ============================================
   SHARED FORM COMPONENT STYLES
   Used by: Transport, TimeRegistrationEdit, RouteCalculator, and other form components
   
   This file contains common patterns shared across multiple form components:
   - CSS Custom Properties (Design Tokens) with rem/em units
   - Modern CSS functions (clamp, min, max) for responsive design
   - User zoom support (WCAG 2.1 compliant)
   - Validation messages
   - Button containers
   - Dark mode support
   - Accessibility features (high contrast, touch targets, keyboard navigation)
   ============================================ */

/* ============================================
   CSS CUSTOM PROPERTIES (DESIGN TOKENS)
   All values use rem for user zoom support
   ============================================ */
:root {
    /* ===== Semantic Colors ===== */
    --tv-danger: var(--palette-red);
    --tv-success: var(--palette-green);
    --tv-info: var(--palette-blue);
    --tv-focus-ring-shadow: color-mix(in srgb, var(--tv-focus-ring) 35%, transparent);
    
    /* ===== Spacing Scale - rem units for zoom support ===== */
    --tv-spacing-xs: 0.25rem;   /* 4px */
    --tv-spacing-sm: 0.5rem;    /* 8px */
    --tv-spacing-md: 0.625rem;  /* 10px */
    --tv-spacing-lg: 0.75rem;   /* 12px */
    --tv-spacing-xl: 1rem;      /* 16px */
    --tv-spacing-2xl: 1.25rem;  /* 20px */
    --tv-spacing-3xl: 1.5rem;   /* 24px */
    --tv-spacing-4xl: 2rem;     /* 32px */
    --tv-spacing-5xl: 2.5rem;   /* 40px */

    /* ===== Form Row Spacing ===== */
    --tv-form-row-gap: 0.75rem;       /* 12px - gap between fields in a row */
    --tv-form-row-spacing: 0.5rem;    /* 8px  - margin-bottom between rows */
    --tv-field-grid-gap: 16px;        /* gap used by .tv-responsive-field-grid, both axes */
    
    /* ===== Border Radius ===== */
    --tv-radius-sm: 0.25rem;  /* 4px */
    --tv-radius-md: 0.375rem; /* 6px */
    --tv-radius-lg: 0.5rem;   /* 8px */
    
    /* ===== Touch Targets (WCAG 2.1 Compliance) ===== */
    --tv-touch-target-min: 2.75rem;        /* 44px - WCAG 2.1 Level AA */
    --tv-touch-target-accessible: 3rem;    /* 48px - WCAG 2.1 Level AAA */
    
    /* ===== Neutral Surfaces =====
       Deliberately achromatic and semi-transparent so a single value reads correctly on both
       light and dark backgrounds — it tints whatever sits behind it rather than asserting a
       colour of its own. */
    --tv-group-surface: color-mix(in srgb, currentColor 10%, transparent);   /* grouped sub-form background */
    --tv-group-border: color-mix(in srgb, currentColor 18%, transparent);    /* grouped sub-form outline */
    --tv-derived-surface: color-mix(in srgb, currentColor 14%, transparent); /* computed, read-only field */
    --tv-hover-surface: color-mix(in srgb, currentColor 15%, transparent);   /* icon-button hover */

    /* ===== Muted Text ===== */
    --tv-text-muted: var(--tv-color-text-secondary);
    --tv-text-primary: var(--color-sf-content-text-color, currentColor);

    /* ===== Font Sizes ===== */
    --tv-font-xs: 0.75rem;   /* 12px */
    --tv-font-sm: 0.8125rem; /* 13px */
    --tv-font-md: 0.875rem;  /* 14px */
    --tv-font-base: 0.9375rem; /* 15px */
    --tv-font-lg: 1rem;      /* 16px */
}

/* ============================================
   LABELS AND REQUIRED INDICATORS
   ============================================ */

.required-indicator {
    color: var(--tv-danger);
}

/* Field caption above an input. Kept lightweight — the emphasis in a form comes from the
   section titles, not from every label shouting. */
.form-label {
    display: block;
    margin-bottom: var(--tv-spacing-xs);
    font-size: var(--tv-font-sm);
    color: var(--tv-form-field-label, hotpink);
}

/* Groups a run of fields under a caption. The rule sits on the title rather than on a wrapper
   so it works inside a grid, where the fields are siblings of the title, not children of it. */
.form-section-title {
    margin: 0;
    padding-bottom: var(--tv-spacing-xs);
    border-bottom: 0.0625rem solid var(--tv-border-color, hotpink); /* 1px */
    font-size: var(--tv-font-sm);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--tv-text-muted, hotpink);
}

/* A section title never opens the form, so it always has a rule above it — except the first. */
.form-section-title:not(:first-child) {
    margin-top: var(--tv-spacing-md);
}

/* ============================================
   FOCUS STATES
   ============================================ */

.e-btn:focus,
.sf-button:focus {
    outline: 0.125rem solid var(--tv-focus-ring); /* 2px */
    box-shadow: 0 0 0 0.1875rem var(--tv-focus-ring-shadow); /* 3px */
}

/* ============================================
   VALIDATION MESSAGES
   Consistent error messaging across all form components
   ============================================ */

.validation-message,
.form-validation-message {
    color: var(--tv-danger);
    font-size: var(--tv-font-xs);
    margin-top: var(--tv-spacing-xs);
    display: block;
    line-height: 1.4;
}

/* High contrast mode validation messages */
@media (forced-colors: active) {
    .validation-message,
    .form-validation-message {
        color: CanvasText;
    }
}

/* ============================================
   VALIDATION SUMMARY BOX
   Consolidated error summary shown after a save attempt
   ============================================ */

.validation-summary {
    background-color: color-mix(in srgb, var(--tv-danger) 15%, transparent);
    border: 0.0625rem solid var(--tv-danger);
    border-radius: var(--tv-radius-md);
    padding: var(--tv-spacing-md) var(--tv-spacing-lg);
    margin-bottom: var(--tv-spacing-xl);
    /* Pulled toward the body text colour rather than used raw: the theme danger red only clears
       WCAG AA against a light backdrop, and over the dark themes' wash it measures ~3:1. Mixing
       toward --tv-body-fg darkens it on light and lightens it on dark, clearing AA on both.
       Note: this must NOT mix toward `currentColor` — inside the `color` property that is a
       circular reference, so the whole declaration is dropped and the raw red silently wins. */
    color: color-mix(in srgb, var(--tv-danger) 55%, var(--tv-body-fg));
    font-size: var(--tv-font-sm);
}

.validation-summary ul {
    margin: var(--tv-spacing-xs) 0 0 var(--tv-spacing-xl);
    padding: 0;
}

@media (forced-colors: active) {
    .validation-summary {
        border: 0.125rem solid CanvasText;
        background-color: Canvas;
        color: CanvasText;
    }
}

/* ============================================
   BUTTON CONTAINERS
   Shared pattern for form action buttons with fluid spacing
   ============================================ */

.form-button-container,
.form-actions,
.button-container,
.dialog-footer {
    display: flex;
    gap: clamp(0.625rem, 1.5vw, 0.75rem); /* 10px ? 12px */
    justify-content: flex-end;
    margin-top: clamp(1rem, 2vw, 2rem); /* 16px ? 32px */
    flex-wrap: wrap;
}

/* Mobile: Stack buttons vertically */
@media (max-width: 47.9375em) { /* 767px / 16 = 47.9375em */
    .form-button-container,
    .form-actions,
    .button-container,
    .dialog-footer {
        flex-direction: column-reverse;
        gap: var(--tv-spacing-md);
        margin-top: var(--tv-spacing-xl);
    }
}

/* ============================================
   BOTTOM-ALIGNED BUTTONS
   For inline form controls (checkboxes, etc.)
   ============================================ */

.bottom-button,
.form-inline-button {
    align-self: end;
}

/* ============================================
   FORM FIELD SPACING
   Consistent fluid spacing between form fields
   ============================================ */

.form-field,
.form-row > div {
    margin-bottom: clamp(1rem, 2vw, 1.25rem); /* 16px ? 20px */
}

/* Remove margin on last child */
.form-field:last-child {
    margin-bottom: 0;
}

/* Grid layouts: Remove margin (use gap instead) */
@media (min-width: 64em) { /* 1024px / 16 = 64em */
    .form-grid .form-field,
    .form-grid > div {
        margin-bottom: 0;
    }
}

/* ============================================
   RESPONSIVE CONTAINER WIDTH
   Single fluid constraint instead of 5 media queries
   ============================================ */

.form-container-responsive {
    max-width: min(100%, 87.5rem); /* 1400px / 16 = 87.5rem */
    padding: clamp(1rem, 2vw, 2.5rem); /* 16px ? 40px */
    margin: 0 auto;
}

/* ============================================
   2-COLUMN GRID UTILITY
   Responsive 2-column layout with fluid gap
   ============================================ */

.form-grid-2col {
    display: flex;
    flex-direction: column;
    gap: clamp(1rem, 2vw, 1.5rem); /* 16px ? 24px */
}

@media (min-width: 64em) { /* 1024px / 16 = 64em */
    .form-grid-2col {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: clamp(1.25rem, 2vw, 2.5rem); /* 20px ? 40px */
        column-gap: clamp(1.5rem, 3vw, 2.5rem); /* 24px ? 40px */
    }
}

/* Full-width items in 2-column grid */
.form-grid-2col .full-width,
.form-grid-2col .span-all {
    grid-column: 1 / -1;
}

/* ============================================
   ACCORDION STYLING (SYNCFUSION)
   Consistent fluid accordion spacing
   ============================================ */

.e-accordion {
    margin-top: var(--tv-form-row-spacing, 0.5rem);
}

.e-accordion .e-acrdn-panel .e-acrdn-content {
    padding: var(--tv-spacing-xs, 0.25rem);
}

/* Mobile: Reduced spacing */
@media (max-width: 47.9375em) { /* 767px / 16 = 47.9375em */
    .e-accordion .e-acrdn-item {
        margin-bottom: var(--tv-spacing-sm);
    }
}

/* ============================================
   SYNCFUSION AUTOCOMPLETE/DROPDOWN POPUP FIX
   Prevents popup from extending beyond the input width
   The popup is rendered in a portal at document root,
   so we need global CSS rules to constrain it.
   
   Using high specificity selectors to override Syncfusion's
   inline styles set via JavaScript.
   ============================================ */

/* 
   APPROACH: Use CSS attribute selectors to target popups by their 
   inline style width and override with max-width constraint.
   Syncfusion sets width inline, but max-width can still constrain it.
*/

/* CSS variable for popup max-width - can be set on parent container */
:root {
    --tv-popup-max-width: 100%;
}

/* 
   e-popup-constrained class: Apply to SfAutoComplete CssClass to enable
   popup width constraints. This ensures the popup doesn't extend beyond
   the viewport or its parent container.
*/

/* High specificity: Target the popup wrapper and content */
body .e-popup.e-ddl.e-popup-open,
body .e-popup.e-autocomplete.e-popup-open,
body .e-popup.e-dropdownbase.e-popup-open,
body .e-popup.e-timepicker.e-popup-open,
html body .e-popup.e-ddl,
html body .e-popup.e-autocomplete,
html body .e-popup.e-dropdownbase,
html body .e-popup.e-timepicker {
    max-width: 100vw !important;
    box-sizing: border-box !important;
    overflow-x: hidden !important;
}

/*
   KEY FIX: Constrain the popup's Syncfusion-set inline width with max-width
   (max-width takes precedence over width when max-width is smaller).
   Deliberately NO `width: auto` here: that would discard the inline width
   (= the input's width) and shrink-wrap the popup around its widest item,
   which defeats text-overflow ellipsis on single-line items — the popup
   grows to fit the text (up to the cap) instead of truncating it.

   Applies to every dropdown, autocomplete and timepicker popup, and that breadth is a
   decision rather than an oversight (#629). A second copy of this rule in the BFF's
   validation-accessibility.css narrowed it to `.tv-task-autocomplete` and documented
   that intent — but this file loads later and the two selectors have equal specificity,
   so the narrowing never took effect. The broad behaviour is what has shipped since the
   initial import, so the narrowing was removed rather than the breadth.

   Consequence worth knowing: `.tv-task-autocomplete` is still set as CssClass on the three
   task autocompletes, but no rule targets it any more. Left as a hook for per-control popup
   behaviour; it styles nothing today.
*/
body .e-popup.e-ddl[style*="width"],
body .e-popup.e-autocomplete[style*="width"],
body .e-popup.e-dropdownbase[style*="width"],
body .e-popup.e-timepicker[style*="width"] {
    min-width: 0 !important;
    max-width: min(var(--tv-popup-max-width, 100%), 100vw) !important;
}

/* Moved here from the BFF's css/validation-accessibility.css (#629), which carried a second,
   forked copy of this whole popup section. These four blocks existed only in that file, so they
   are the part of it worth keeping.

   Placed HERE, above the TimePicker section, to hold the order they had before:
   validation-accessibility.css was linked before this file, so every rule in it lost to every
   conflicting rule here. Keeping that relationship is what makes the move a no-op.

   The position turns out not to be decisive, which is worth writing down so nobody "tidies" it
   back and assumes they proved something: a timepicker popup carries none of `e-ddl`,
   `e-autocomplete` or `e-dropdownbase`, so the `.e-list-item` rules below cannot match it at
   all. The one selector here that does match a timepicker (`body .e-popup ul.e-ul`) sets exactly
   the values the TimePicker block sets itself. Checked in the browser rather than reasoned:
   computed styles for e-ddl, e-autocomplete, e-dropdownbase and e-timepicker are identical
   before and after this change.

   !important throughout for the same reason as the rest of the section: Syncfusion sets these
   dimensions inline from JavaScript, and nothing without it reaches them. */

/* Target the UL list element */
body .e-popup.e-ddl ul.e-list-parent,
body .e-popup.e-autocomplete ul.e-list-parent,
body .e-popup ul.e-ul {
    max-width: 100% !important;
    overflow-x: hidden !important;
    box-sizing: border-box !important;
}

/* Ensure popup list items wrap text or truncate instead of extending */
body .e-popup.e-ddl .e-list-item,
body .e-popup.e-autocomplete .e-list-item,
body .e-popup.e-dropdownbase .e-list-item,
body .e-popup.e-ddl li.e-list-item,
body .e-popup.e-autocomplete li.e-list-item {
    white-space: normal !important;
    word-wrap: break-word !important;
    overflow-wrap: break-word !important;
    word-break: break-word !important;
    max-width: 100% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
}

/* Target item text spans - truncate with ellipsis */
body .e-popup.e-ddl .e-list-item span,
body .e-popup.e-autocomplete .e-list-item span,
body .e-popup .e-list-item > span {
    display: block !important;
    max-width: 100% !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

/* Mobile: Constrain popup with viewport padding.
   Kept at 767px rather than the 47.9375em the blocks around it use: em in a media query resolves
   against the browser's default font size, not :root, so the two agree only at a 16px default.
   Converting would shift the breakpoint for anyone who has changed that, and this move is meant
   to be visually identical. Unifying them is its own decision. */
@media (max-width: 767px) {
    body .e-popup.e-ddl,
    body .e-popup.e-autocomplete,
    body .e-popup.e-dropdownbase,
    html body .e-popup.e-ddl.e-popup-open,
    html body .e-popup.e-autocomplete.e-popup-open,
    body .e-popup.e-ddl[style*="width"],
    body .e-popup.e-autocomplete[style*="width"] {
        /* Cap only — keep Syncfusion's inline width (= input width) so
           single-line items truncate instead of growing the popup. */
        max-width: calc(100vw - 16px) !important;
    }
}

/* ============================================
   SYNCFUSION TIMEPICKER POPUP FIX
   TimePicker popup has a different structure than autocomplete.
   These rules constrain the time selection popup to prevent it
   from extending to the screen edge.
   ============================================ */

/* Target the TimePicker popup specifically */
body .e-timepicker.e-popup,
body .e-timepicker-wrapper .e-popup,
html body .e-popup.e-timepicker {
    width: auto !important;
    min-width: 10rem !important; /* Minimum width for time values */
    max-width: min(20rem, 100vw - 1rem) !important; /* Cap at reasonable width */
    box-sizing: border-box !important;
}

/* Target TimePicker popup when opened with inline width style */
body .e-timepicker.e-popup[style*="width"],
body div.e-popup.e-control.e-timepicker[style*="width"] {
    width: auto !important;
    min-width: 10rem !important;
    max-width: min(20rem, 100vw - 1rem) !important;
}

/* Target the inner time list container */
body .e-timepicker.e-popup .e-content,
body .e-timepicker.e-popup .e-list-parent,
body .e-timepicker.e-popup .e-ul {
    max-width: 100% !important;
    overflow-x: hidden !important;
    box-sizing: border-box !important;
}

/* Target time list items */
body .e-timepicker.e-popup .e-list-item,
body .e-timepicker.e-popup li.e-list-item {
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    max-width: 100% !important;
}

/* Mobile: Ensure time picker popup fits within viewport */
@media (max-width: 47.9375em) { /* 767px / 16 = 47.9375em */
    body .e-timepicker.e-popup,
    body .e-timepicker.e-popup[style*="width"],
    html body .e-popup.e-timepicker {
        max-width: calc(100vw - 1rem) !important;
        width: auto !important;
    }
}

/* Target the inner list container with high specificity */
body .e-popup.e-ddl .e-content.e-dropdownbase,
body .e-popup.e-autocomplete .e-content.e-dropdownbase,
body .e-popup.e-ddl .e-dropdownbase.e-content,
body .e-popup.e-autocomplete .e-dropdownbase.e-content,
html body .e-popup .e-content {
    max-width: 100% !important;
    overflow-x: hidden !important;
    box-sizing: border-box !important;
}

/* ============================================
   HIGH CONTRAST MODE (ACCESSIBILITY)
   Windows High Contrast Mode support
   ============================================ */

@media (forced-colors: active) {
    /* Ensure visible borders */
    .form-container,
    .transport-container,
    .time-registration-container {
        border: 0.0625rem solid CanvasText; /* 1px */
        padding: var(--tv-spacing-sm);
    }
    
    /* Ensure visible button borders */
    .form-button-container .e-btn,
    .button-container .e-btn,
    .dialog-footer .e-btn {
        border: 0.125rem solid ButtonText; /* 2px */
    }
    
    /* Ensure text is visible */
    .form-label {
        color: CanvasText;
    }
}

/* ============================================
   LOADING STATE
   Shared spinner + label pattern used by EntityDetailPage, EntityListPage, EntityDetail
   ============================================ */

.tv-loading-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 500px;
    gap: 16px;
}

.tv-loading-text {
    font-size: 1.1rem;
    color: var(--tv-accent, hotpink);
}

/* ============================================
   ALERT BANNERS
   Shared .tv-alert + modifier pattern
   ============================================ */

.tv-alert {
    padding: 12px 16px;
    border-radius: var(--tv-radius-sm, 4px);
    margin-bottom: 8px;
    /* A translucent wash of the accent rather than a pinned pastel: the banner then sits on the
       current panel surface, so inherited body text stays legible on light, dark and high
       contrast alike. The previous fixed backgrounds needed a pinned foreground to compensate. */
}

.tv-alert-warning {
    background-color: color-mix(in srgb, var(--palette-amber) 18%, transparent);
    border-left: 4px solid var(--palette-amber);
}

.tv-alert-info {
    background-color: color-mix(in srgb, var(--palette-accent) 18%, transparent);
    border-left: 4px solid var(--palette-accent);
}

/* ============================================
   UTILITY CLASSES
   ============================================ */

/* Spacing utilities */
.mt-sm { margin-top: var(--tv-spacing-sm); }
.mt-md { margin-top: var(--tv-spacing-md); }
.mt-lg { margin-top: var(--tv-spacing-lg); }
.mt-xl { margin-top: var(--tv-spacing-xl); }
.mt-2xl { margin-top: var(--tv-spacing-2xl); }

.mb-sm { margin-bottom: var(--tv-spacing-sm); }
.mb-md { margin-bottom: var(--tv-spacing-md); }
.mb-lg { margin-bottom: var(--tv-spacing-lg); }
.mb-xl { margin-bottom: var(--tv-spacing-xl); }
.mb-2xl { margin-bottom: var(--tv-spacing-2xl); }

/* Gap utilities */
.gap-sm { gap: var(--tv-spacing-sm); }
.gap-md { gap: var(--tv-spacing-md); }
.gap-lg { gap: var(--tv-spacing-lg); }
.gap-xl { gap: var(--tv-spacing-xl); }
.gap-2xl { gap: var(--tv-spacing-2xl); }

/* Text utilities. The status hues are fill/glyph-grade, so as running text they are pulled
   toward the body colour to clear AA on light, dark and high contrast alike. */
.text-error { color: color-mix(in srgb, var(--tv-danger) 55%, var(--tv-body-fg)); }
.text-success { color: color-mix(in srgb, var(--tv-success) 55%, var(--tv-body-fg)); }
.text-info { color: color-mix(in srgb, var(--tv-info) 55%, var(--tv-body-fg)); }

/* ============================================
   PRINT STYLES
   Form printing support
   ============================================ */

@media print {
    /* Hide interactive elements */
    .form-button-container,
    .button-container,
    .dialog-footer,
    .bottom-button,
    .e-spin-overlay {
        display: none;
    }
    
    /* Ensure proper page breaks */
    .form-container,
    .time-registration-container,
    .transport-container,
    .outlays-container {
        page-break-inside: avoid;
    }
    
    /* High contrast for printing */
    .form-section {
        border: 0.0625rem solid #000; /* 1px */
        background: white;
    }
}

/* ============================================
   REDUCED MOTION (ACCESSIBILITY)
   Respect user's motion preferences
   ============================================ */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ============================================
   RESPONSIVE FIELD GRID
   Usage: class="tv-responsive-field-grid" style="--tv-grid-columns: N;"
   where N is a *ceiling* on the column count — normally
   DeviceBreakpoint.ToFieldColumnCount(), clamped to the number of fields so a short
   row is not spread out. The actual count follows the container width.
   Override --tv-field-min-width on a single grid to change where columns break.
   ============================================ */
.tv-responsive-field-grid {
    --tv-field-min-width: 14rem;
    display: grid;
    /* Fluid, but capped. The second max() term is the width one track would have if the
       container were split into exactly --tv-grid-columns equal columns; while it is the
       larger of the two, auto-fill can fit no more than that many tracks, which is what
       enforces the ceiling. Once the container narrows enough that it drops below
       --tv-field-min-width the minimum takes over and the column count falls off
       continuously instead of in breakpoint-sized steps. min(100%, ...) keeps a container
       narrower than one field from overflowing. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, max(var(--tv-field-min-width), (100% - (var(--tv-grid-columns, 1) - 1) * var(--tv-field-grid-gap, 16px)) / var(--tv-grid-columns, 1))), 1fr));
    gap: var(--tv-field-grid-gap, 16px);
    width: 100%;
    align-items: start;
}
.tv-responsive-field-grid > * { width: 100%; }

/* Collection editors (DynamicDataGrid) span all columns — they render as full-width grids */
.tv-responsive-field-grid > *:has(.tv-dynamic-datagrid) {
    grid-column: 1 / -1;
}

/* Custom-fields editors span all columns — they render their own inner responsive grid */
.tv-responsive-field-grid > *:has(.custom-field-input) {
    grid-column: 1 / -1;
}

/* Section editors (registered via CustomPropertyEditorRegistry.RegisterSection) own several
   related properties and lay them out themselves, so they span all columns. */
.tv-responsive-field-grid > *:has(.tv-entity-section) {
    grid-column: 1 / -1;
}

/* ============================================
   CUSTOMER INVOICE ADDRESSING SECTION
   Section editor owning the mutually exclusive invoice-address choice
   ============================================ */

.customer-invoice-address {
    display: flex;
    flex-direction: column;
    gap: var(--tv-spacing-md, 1rem);
}

.customer-invoice-address-choice {
    display: flex;
    flex-direction: column;
    gap: var(--tv-spacing-xs, 0.25rem);
}

.customer-invoice-address-label {
    font-weight: 600;
    margin-bottom: var(--tv-spacing-xs, 0.25rem);
}

/* Read-only preview of the address an invoice would actually carry. Bordered rather than filled,
   so it reads as informational and stays legible on both the light and dark themes. */
.customer-invoice-address-preview {
    padding: var(--tv-spacing-sm, 0.5rem) var(--tv-spacing-md, 1rem);
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-radius-sm, 4px);
}

.customer-invoice-address-preview-heading {
    font-weight: 600;
    margin-bottom: var(--tv-spacing-xs, 0.25rem);
}

.customer-invoice-address-empty {
    font-style: italic;
    opacity: 0.75;
}

/* Inline edit toggle: switches a derived field to hand-entered (e.g. transport km).
   Sits inside the field wrapper so it reads as belonging to that input. */
/* Inner row holding the km editor and its override pencil. Deliberately NOT also a .form-field:
   that class sets flex-direction: column, which would lay this out vertically and (with
   align-items: flex-end) right-align the input at content width instead of filling. */
.transport-distance-field {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    gap: var(--tv-spacing-xs, 0.25rem);
}

.transport-distance-field > :first-child {
    flex: 1;
    min-width: 0;
}

/* ============================================
   DERIVED (COMPUTED) FIELDS
   A value produced by the app rather than typed by the user. Syncfusion renders read-only
   inputs almost identically to editable ones — especially in dark themes — so without this
   there is no way to tell whether a number is yours or the system's.
   Background only: the text itself stays at full contrast, because a derived value is still
   something the user needs to read and check.
   ============================================ */

.tv-field-derived .e-input-group,
.tv-field-derived.e-input-group,
.tv-field-derived .e-control-wrapper {
    background-color: var(--tv-derived-surface, hotpink);
}

.tv-field-derived input {
    cursor: default;
}

/* Hint beneath a field, e.g. flagging that km was hand-entered. */
.tv-field-hint {
    font-size: var(--tv-font-xs);
    color: var(--tv-text-muted, hotpink);
    margin-top: var(--tv-spacing-xs);
    line-height: 1.3;
}

@media (forced-colors: active) {
    /* Background carries no meaning in forced-colors, so fall back to a visible border. */
    .tv-field-derived .e-input-group,
    .tv-field-derived.e-input-group,
    .tv-field-derived .e-control-wrapper {
        border: 0.125rem dashed CanvasText;
    }
}

/* Square icon button sized to sit on the input's own line rather than the wrapper's bottom
   edge, and tall enough to be a comfortable hit target. The floating label occupies space
   above the input, so the row is aligned flex-end and the button's height is matched to the
   Syncfusion input rather than left to shrink to the glyph. */
.tv-inline-edit-toggle {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    /* Matches the Syncfusion textbox height so the two line up on the same baseline. */
    height: 2rem;
    width: 2rem;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Without this the glyph inherits the form's small font and reads as an artefact. */
    font-size: 1.125rem;
    line-height: 1;
    /* Nudges the icon off the input's bottom border onto its vertical centre. */
    margin-bottom: 0.25rem;
    color: var(--tv-text-muted, hotpink);
    border-radius: var(--tv-radius-sm, 0.25rem);
}

.tv-inline-edit-toggle:hover {
    background: var(--tv-hover-surface, hotpink);
    color: var(--tv-text-primary, hotpink);
}

/* Pressed = the field is currently hand-entered. */
.tv-inline-edit-toggle[aria-pressed="true"] {
    color: var(--color-sf-primary, #0d6efd);
}

.tv-inline-edit-toggle:focus-visible {
    outline: 2px solid var(--color-sf-primary, #0d6efd);
    outline-offset: 2px;
}
