
/* Changed: Raise CDK overlay above sidebar (z-index: 1030) so dialogs are not covered by the nav */
.cdk-overlay-container {
  z-index: 1050 !important;
}

/* Tiny icons for chip remove buttons */
.mat-mdc-chip .mat-icon,
.mat-mdc-chip-remove .mat-icon {
  font-size: 14px !important;
  width: 14px !important;
  height: 14px !important;
}

/* Exception: spa-groups chips should have larger icons */
spa-groups .mat-mdc-chip .mat-icon {
  font-size: 20px !important;
  width: 20px !important;
  height: 20px !important;
  padding-top: 3px !important;
  margin-right: 6px !important;
}

spa-groups .mat-mdc-chip .chip-additional-icon {
  font-size: 18px !important;
  width: 18px !important;
  height: 18px !important;
  font-size: 18px !important;
  width: 18px !important;
  height: 18px !important;
}

/* Tiny icons in option components - removed padding and reduced button size */
.tin-row .mat-mdc-icon-button,
.tin-row .mat-icon-button {
  width: 14px !important;
  height: 14px !important;
  padding: 0 !important;
  margin: 0 !important;
  line-height: 14px !important;
}

.tin-row .mat-mdc-icon-button .mat-icon,
.tin-row .mat-icon-button .mat-icon {
  font-size: 14px !important;
  width: 14px !important;
  height: 14px !important;
}

/* Centralized suffix-icons styling for consistency across all components */
.suffix-icons {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
}

.suffix-icons button {
  opacity: 0;
  transition: opacity 0.3s ease;
}

:hover .suffix-icons button {
  opacity: 1;
}

/* Override tin-row styles when inside suffix-icons to maintain consistency */
.tin-row .suffix-icons .mat-mdc-icon-button,
.tin-row .suffix-icons .mat-icon-button,
.suffix-icons .mat-mdc-icon-button,
.suffix-icons .mat-icon-button {
  width: 20px !important;
  height: 20px !important;
  padding: 0 !important;
  line-height: 20px !important;
  margin: 0 !important;
  display: inline-flex !important; /* Changed: Center icon within button */
  align-items: center !important; /* Changed: Vertical centering */
  justify-content: center !important; /* Changed: Horizontal centering */
  overflow: hidden !important; /* Changed: Clip oversized touch target to button bounds */
}

/* Changed: Constrain touch target to match button size so clicks align with visible icon */
.suffix-icons .mat-mdc-button-touch-target {
  width: 100% !important;
  height: 100% !important;
}

.tin-row .suffix-icons .mat-mdc-icon-button .mat-icon,
.tin-row .suffix-icons .mat-icon-button .mat-icon,
.suffix-icons .mat-mdc-icon-button .mat-icon,
.suffix-icons .mat-icon-button .mat-icon,
.suffix-icons > .mat-icon, /* Changed: Size standalone icons in suffix (e.g. bitwise selected icons) */
.tinyIcon {
  font-size: 14px !important;
  width: 14px !important;
  height: 14px !important;
  line-height: 14px !important;
}

/* Tiny datepicker calendar icon */
.mat-datepicker-toggle {
  display: inline-flex !important;
  align-items: center !important;
  margin-left: 0 !important;
}

.mat-datepicker-toggle button,
.mat-mdc-icon-button.mat-datepicker-toggle-default-icon {
  width: 20px !important;
  height: 20px !important;
  padding: 0 !important;
  margin: 0 !important;
}

.mat-datepicker-toggle .mat-icon,
.mat-datepicker-toggle-default-icon {
  font-size: 14px !important;
  width: 14px !important;
  height: 14px !important;
  line-height: 14px !important;
}

/* Set datepicker calendar popup background to white */
.mat-datepicker-content,
.mat-datepicker-content-container {
  background-color: white !important;
}

/* Keep filter refresh icon at normal size - exclude from tiny icon styling */
.filter-container .mat-icon-button,
.filter-container .mat-mdc-icon-button {
  width: 40px !important;
  height: 40px !important;
  padding: 8px !important;
}

.filter-container .mat-icon-button .mat-icon,
.filter-container .mat-mdc-icon-button .mat-icon,
.refreshIcon {
  font-size: 24px !important;
  width: 24px !important;
  height: 24px !important;
  line-height: 24px !important;
}

/* Exception: Keep clear icon in filter suffix tiny */
.filter-container .suffix-icons .mat-icon-button,
.filter-container .suffix-icons .mat-mdc-icon-button {
  width: 20px !important;
  height: 20px !important;
  padding: 0 !important;
}

.filter-container .suffix-icons .mat-icon {
  font-size: 14px !important;
  width: 14px !important;
  height: 14px !important;
}

/* Keep search button at normal size - same styling as filter refresh button */
.tin-between .tin-end .mat-icon-button,
.tin-between .tin-end .mat-mdc-icon-button {
  width: 40px !important;
  height: 40px !important;
  padding: 8px !important;
}

.tin-between .tin-end .mat-icon-button .mat-icon,
.tin-between .tin-end .mat-mdc-icon-button .mat-icon {
  font-size: 24px !important;
  width: 24px !important;
  height: 24px !important;
  line-height: 24px !important;
}

/* Set html and body to fill viewport and remove default margins */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

/* Apply modern thin font weight to all headings globally */
h1, h2, h3, h4, h5, h6 {
  font-weight: 300;
}

table {
  width: 100%;
}

.list-group {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.tin-input {
  display: flex;
  flex-direction: column;
}

.tin-input , .tin-col > * {
  width: 100%;
}

.tin-bg-color {
  background-color: #f2f7ff;
  background-size: 100%;
  background-size: 100%;
  min-height: 100vh; /* Use 100vh instead of 100% to fill viewport */
  height: auto;
}



.tin-side-image {
  /* background-image: url("~/assets/sidebar.jpg"); */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 400'%3E%3Crect width='200' height='400' fill='%23ffffff'/%3E%3Cpath d='M20 0 Q40 50, 20 100 T20 200 T20 300 T20 400' stroke='%234a90e2' fill='none' stroke-width='1' opacity='0.3'/%3E%3Cpath d='M60 0 Q90 80, 60 160 T60 320 T60 400' stroke='%2350e3c2' fill='none' stroke-width='1' opacity='0.3'/%3E%3Cpath d='M100 0 Q140 100, 100 200 T100 400' stroke='%23b8e986' fill='none' stroke-width='1' opacity='0.3'/%3E%3Cpath d='M140 0 Q180 120, 140 240 T140 400' stroke='%239013fe' fill='none' stroke-width='1' opacity='0.3'/%3E%3Ccircle cx='20' cy='100' r='2' fill='%234a90e2' opacity='0.5'/%3E%3Ccircle cx='60' cy='160' r='2' fill='%2350e3c2' opacity='0.5'/%3E%3Ccircle cx='100' cy='200' r='2' fill='%23b8e986' opacity='0.5'/%3E%3Ccircle cx='140' cy='240' r='2' fill='%239013fe' opacity='0.5'/%3E%3Cpath d='M20 100 H140' stroke='%234a90e2' stroke-width='1' opacity='0.2'/%3E%3Cpath d='M60 160 H180' stroke='%2350e3c2' stroke-width='1' opacity='0.2'/%3E%3Cpath d='M20 200 H100' stroke='%23b8e986' stroke-width='1' opacity='0.2'/%3E%3Cpath d='M60 240 H140' stroke='%239013fe' stroke-width='1' opacity='0.2'/%3E%3C/svg%3E");
  /* background-size: 100%;
  background-repeat: repeat-y;
  background-size: 100%;
  min-height: 100%;
  height: auto; */

  min-height: 100%;
  /* opacity: 0.5; */
  background-size: cover;
  /* background-position: center; */
  /* background-attachment: fixed; */
}




.tin-input-row {
  display: flex;
  flex-direction: row !important;
  align-content: center !important;
  align-items: center !important;
  gap: 1em;
}

.tin-grid button {
  width: 100%;
}

.tin-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

.tin-end {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.tin-between {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
}

.tin-row {
  display: flex;
  flex-direction: row;
  align-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 1em;
}

.tin-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.tin-col button{
  min-width: 50%;
}

.highlight{
  box-shadow: 0 0 0 1px red;
}

.tin-grid {
  display: grid;
  gap: 1rem;
  row-gap: 10px;
  grid-template-columns: 1fr 1fr;
  /* grid-auto-columns:auto; */
  /* grid-template-columns: minmax(auto, 100px) auto; */
  align-items: start; /* Changed: Align fields to top for consistent alignment when hints/errors appear */
}

.tin-grid > * {
  width: 100%;
}

.tin-grid button {
  width: 50%;
}

.span-col,
.span-col-2 {
  grid-column: span 2;
}

.span-col-center {
  grid-column: span 2;
  display: flex;
  justify-content: center;
  align-items: center;
}




@media (max-width: 600px) {
  .tin-grid {
    display: flex;
    flex-direction: column;
    gap: 0.5rem; /* Changed: Add spacing between components on small screens */
  }

  .tin-grid,
  .tin-grid button,
  .tin-col button,
  .tin-grid-auto button {
    width: 100%;
  }
}

.tin-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.tin-grid-auto {
  --min-col-size: 22rem;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(var(--min-col-size), 100%), 1fr)
  );
}

.tin-reel {
  --gap: 1rem;
  display: flex;
  gap: var(--gap);
  grid-auto-flow: column;
  grid-auto-columns: calc(30% - (var(--gap) / 2));
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  scroll-padding: var(--gap);
}

.reel > * {
  scroll-snap-align: start;
}

.tin-main-sidebar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 1em;
}

.tin-main-sidebar > :first-child {
  flex-basis: 500px;
  flex-grow: 9999;
}

.tin-main-sidebar > :last-child {
  flex-basis: 300px;
  flex-grow: 1;
}

/* Global font-size for all Angular Material form fields (Material 3) */
.mat-mdc-form-field {
  font-size: 14px !important;
}

.mat-mdc-text-field-wrapper {
  font-size: 14px !important;
}

.mat-mdc-form-field .mat-mdc-input-element {
  font-size: 14px !important;
  
}

.mat-mdc-form-field .mat-mdc-floating-label {
  font-size: 14px !important;
}

/* Global font-size for mat-select dropdown components */
.mat-mdc-select {
  font-size: 14px !important;
}

.mat-mdc-select-value {
  font-size: 14px !important;
}

.mat-mdc-select-placeholder {
  font-size: 14px !important;
}

.mat-mdc-select-trigger {
  font-size: 14px !important;
}

/* Dropdown panel and options */
.mat-mdc-select-panel {
  font-size: 14px !important;
  background-color: white !important;
}

.mat-mdc-option {
  font-size: 14px !important;
}

.mat-mdc-option .mdc-list-item__primary-text {
  font-size: 14px !important;
}

/* Global icon size for buttons and tables */
button mat-icon,
.mat-mdc-button mat-icon,
.mat-mdc-icon-button mat-icon,
.mat-mdc-fab mat-icon,
.mat-mdc-mini-fab mat-icon,
table mat-icon,
.mat-mdc-table mat-icon {
  font-size: 20px !important;
  width: 20px !important;
  height: 20px !important;
  line-height: 20px !important;
}

.mat-mdc-form-field-infix {
  padding-top: 8px !important; /* Compact top padding */
  padding-bottom: 8px !important; /* Compact bottom padding */
  min-height: auto !important; /* Remove default min-height */
}

.mat-mdc-text-field-wrapper {

  padding-top: 0px !important; /* Remove extra padding above input */
  padding-bottom: 0px !important; /* Remove extra padding below input */

  /* FORM-4 — restore the horizontal inset on BOTH sides, always.
     Material zeroes padding-right when a field has an icon suffix, and padding-left when it has a prefix,
     because it assumes the adjacent control is a stock 48px mat-icon-button whose own padding supplies the
     visual gap. This library breaks that assumption on purpose: `.suffix-icons .mat-mdc-icon-button` is forced
     to 20x20 with `padding: 0` so the icons read as small. The gap Material handed over therefore never came
     back, and every measured field had its trailing icon sitting EXACTLY 0.0px from the border — the select
     arrows, the bitwise action icons, the password toggle, and the money field's "USD" prefix hard against
     the left edge.
     It affected every field, not only the ones with visible icons: all 13 components render their
     `<div matSuffix class="suffix-icons">` unconditionally, so `mat-mdc-form-field-has-icon-suffix` is
     present even when spa-suffix draws nothing, and plain text fields were left with a 4px right inset
     against a 16px left one.
     16px matches the inset Material already applies on the unadorned side, so adorned and unadorned fields
     line up with each other instead of each having its own margin. */
  padding-left: 16px !important;
  padding-right: 16px !important;
}

/* Center floating label vertically only when empty and not focused */
.mat-mdc-floating-label:not(.mdc-floating-label--float-above) {
  top: 50% !important;
  transform: translateY(-50%) !important;
}

.mat-mdc-form-field-subscript-wrapper {
  margin-top: 0px !important; /* Minimal top margin from hint area */
  margin-bottom: 5px !important; /* Minimal top margin from hint area */
}

/* ---------------------------------------------------------------------------
   FORM FIELD ALIGNMENT (FORM-1/2/3)

   Every form row is a .tin-grid cell, and the cells are top-aligned. That keeps
   the CONTROL BAND (the boxed part of a mat-form-field) of two neighbours level
   only while both cells start at the same offset — which is why any component
   that is not a mat-form-field, and any change in a field's own height, shows up
   as a misaligned row.

   Two numbers govern the whole thing, measured off the rendered form rather than
   assumed: the control band is 40px (infix 8px + input 24px + 8px above), and a
   hint or error adds exactly 20px of subscript beneath it.
   --------------------------------------------------------------------------- */
:root {
  --tin-field-band: 40px;      /* height of a mat-form-field's boxed control area */
}

/* FORM-2 (revised) — the subscript band is no longer reserved.

   The original FORM-2 rule pinned every spa-form subscript open at 20px so a form
   never reflowed when a hint or error appeared. In practice most forms have no
   hints at all, so every row paid 20px of permanent dead space and dialogs ran out
   of vertical room (worst on single-column mobile, where each field paid it).

   Decision reversed: let subscriptSizing="dynamic" (already set on all 13 field
   components) collapse the subscript to 0 when empty and expand it only while a
   hint or error is actually showing. Row alignment survives without the
   reservation because .tin-grid top-aligns its cells (align-items: start) and a
   grid row grows as one unit — when one field shows an error, its neighbour's
   control band stays level with it and the rows below shift together. The
   accepted cost is that the form reflows at the moment an error appears/clears.

   No margin-bottom override here anymore: the global 5px subscript margin above
   now applies inside spa-form too, giving rows a 15px rhythm (10px grid row-gap
   + 5px) instead of the old 30px. */

/* FORM-1 / FORM-3 — components that are NOT mat-form-fields must still centre
   themselves on the control band of the field beside them.

   spa-label rendered as a bare text block anchored to the top of its cell, so a
   read-only label sat several pixels above the input text it was paired with (the
   reported "Balance : USD 500.00" row). spa-check happens to be 40px today and so
   lines up by coincidence; pinning it to the same variable makes that a rule
   rather than a coincidence, and keeps it true if Material's density changes.

   Scoped to spa-form ON PURPOSE. Both components are also used outside forms —
   spa-check in table-row, title-actions and page toolbars, spa-label in the
   profile and tenant-settings pages — where a 40px floor and a flex host would
   inflate table rows and change inline flow for no benefit. */
spa-form spa-label,
spa-form spa-check {
  display: flex;
  align-items: center;
  min-height: var(--tin-field-band);
}

/* Composite subfields share a row via flex, which defaults to align-items:stretch —
   a checkbox or label among boxed inputs would stretch instead of sitting on the
   band. Match the grid's top alignment so composites align by the same rule. */
.composite-field-group {
  align-items: flex-start;
}

/* Set default white background for all Material cards */
.mat-mdc-card,
mat-card {
  background-color: white !important;
}

/* Set default white background for Material menu panels */
.mat-mdc-menu-panel,
.mat-menu-panel {
  background-color: white !important;
}

/* Target the button label text within menu items */
.mat-mdc-menu-content button,
.mat-mdc-menu-content .mat-mdc-menu-item,
.mat-mdc-menu-content .mat-mdc-menu-item .mat-mdc-button-persistent-ripple,
.mat-mdc-menu-content .mat-mdc-menu-item > span {
  font-weight: 400 !important;
}

/* Set default white background for Material tables */
.mat-mdc-table,
mat-table,
.mat-table {
  background-color: white !important;
}

/* Grey table borders and lines */
.mat-mdc-table,
mat-table,
.mat-table {
  border-collapse: collapse !important;
}

.mat-mdc-table th,
.mat-mdc-table td,
mat-table th,
mat-table td,
.mat-table th,
.mat-table td {
  border-bottom: 1px solid #e0e0e0 !important;
}

/* Grey column header text */
.mat-mdc-table th,
.mat-mdc-header-row th,
.mat-mdc-header-cell,
mat-table th,
mat-header-row th,
mat-header-cell,
.mat-table th,
.mat-header-row th,
.mat-header-cell {
  color: #757575 !important;
  font-weight: 500 !important;
  font-size: 12px !important;
}

/* Compact table row heights */
.mat-mdc-table tr,
.mat-mdc-table .mat-mdc-row,
.mat-mdc-table .mat-mdc-header-row,
.mat-mdc-row,
.mat-mdc-header-row,
mat-row,
mat-header-row,
.mat-row,
.mat-header-row,
tr.mat-mdc-row,
tr.mat-mdc-header-row {
  height: 50px !important;
  min-height: 40px !important;
  /* max-height: 40px !important; */
}

.mat-mdc-table td,
.mat-mdc-table th,
.mat-mdc-table .mat-mdc-cell,
.mat-mdc-table .mat-mdc-header-cell,
.mat-mdc-cell,
.mat-mdc-header-cell,
mat-cell,
mat-header-cell,
.mat-cell,
.mat-header-cell,
td.mat-mdc-cell,
th.mat-mdc-header-cell {
  padding-top: 4px !important;
  padding-bottom: 4px !important;
  padding-left: 8px !important;
  padding-right: 8px !important;
  height: 40px !important;
  line-height: 20px !important;
  box-sizing: border-box !important;
}

/* Add extra left padding to first column cells */
.mat-mdc-table td:first-child,
.mat-mdc-table th:first-child,
.mat-mdc-cell:first-child,
.mat-mdc-header-cell:first-child {
  padding-left: 16px !important;
}

/* Compact chip buttons in table cells */
.mat-mdc-table .mat-stroked-button,
.mat-mdc-table .mat-mdc-outlined-button,
.mat-mdc-cell .mat-stroked-button,
.mat-mdc-cell .mat-mdc-outlined-button {
  height: 35px !important;
  min-height: 35px !important;
  padding: 0 12px !important;
  line-height: 35px !important;
  font-size: 13px !important;
}

/* Compact icon buttons in table action columns */
.mat-mdc-table .mat-mdc-icon-button,
.mat-mdc-table .mat-icon-button,
.mat-mdc-cell .mat-mdc-icon-button,
.mat-mdc-cell .mat-icon-button {
  width: 32px !important;
  height: 32px !important;
  padding: 4px !important;
  line-height: 32px !important;
}

.mat-mdc-table .mat-mdc-icon-button .mat-icon,
.mat-mdc-table .mat-icon-button .mat-icon,
.mat-mdc-cell .mat-mdc-icon-button .mat-icon,
.mat-mdc-cell .mat-icon-button .mat-icon {
  font-size: 20px !important;
  width: 20px !important;
  height: 20px !important;
  line-height: 20px !important;
}

/* Material 3 compliant elevation classes for backward compatibility */
.mat-elevation-z0 {
  box-shadow: none !important;
}

.mat-elevation-z1 {
  box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
              0px 1px 1px 0px rgba(0, 0, 0, 0.14),
              0px 1px 3px 0px rgba(0, 0, 0, 0.12) !important;
}

.mat-elevation-z5 {
  box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
              0px 5px 8px 0px rgba(0, 0, 0, 0.14),
              0px 1px 14px 0px rgba(0, 0, 0, 0.12) !important;
}

.mat-elevation-z9 {
  box-shadow: 0px 5px 6px -3px rgba(0, 0, 0, 0.2),
              0px 9px 12px 1px rgba(0, 0, 0, 0.14),
              0px 3px 16px 2px rgba(0, 0, 0, 0.12) !important;
}

/* Align dialog action buttons to the left with wrap support */
.mat-mdc-dialog-actions,
.mat-dialog-actions {
  justify-content: flex-start !important;
  flex-wrap: wrap !important; /* Changed: Allow buttons to wrap on small screens */
  gap: 8px !important; /* Changed: Spacing between button groups when they wrap */
}

/* Changed: Ensure individual buttons inside dialog actions wrap with spacing */
.mat-mdc-dialog-actions > div,
.mat-dialog-actions > div {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* Changed: Material gives every dialog-action button after the first a margin-left of 8px. On a wrapped row that
   margin lands on the button that starts the new line, indenting it 8px past Submit. The gap above already spaces
   them, so zero the margin and every row starts flush on the left. */
.mat-mdc-dialog-actions .mat-button-base + .mat-button-base,
.mat-mdc-dialog-actions .mat-mdc-button-base + .mat-mdc-button-base,
.mat-dialog-actions .mat-button-base + .mat-button-base {
  margin-left: 0 !important;
  margin-right: 0 !important;
}

/* Changed: Material's invisible 48px tap area (.mat-mdc-button-touch-target) is wider than the 40px/32px icon
   buttons this app renders, so a button sitting flush against the right edge of a scroll container (e.g. a table's
   refresh icon inside a tab) pushed 4px of nothing past the edge and summoned a phantom horizontal scrollbar.
   Capping it at the button's own width keeps the tap area but lets scrollbars appear only for real content. */
.mat-mdc-button-touch-target {
  max-width: 100%;
}

/* Override for spa-groups chip icons - maintain larger size */
spa-groups .chip-icon,
spa-groups .chip-additional-icon {
  font-size: inherit !important;
  width: inherit !important;
  height: inherit !important;

}

/* Ensure mat-chip icons in spa-groups are not affected by global tiny icon rules */
spa-groups mat-chip .mat-icon {
  font-size: inherit !important;
  width: inherit !important;
  height: inherit !important;

}

/* Read-only field styling: make readonly inputs look like Material disabled controls.
   Applied via [class.spa-readonly] on mat-form-field in text/number/money/text-mask/
   text-area/text-single/text-multi/email/date/datetime components, matching the
   disabled look mat-select and mat-checkbox already get. Uses MDC disabled tokens. */
.mat-mdc-form-field.spa-readonly .mdc-text-field--filled {
  background-color: rgba(0, 0, 0, 0.06); /* slightly darker than MDC's 0.04 so the disabled state is unmistakable */
}
.mat-mdc-form-field.spa-readonly .mdc-text-field--outlined .mdc-notched-outline__leading,
.mat-mdc-form-field.spa-readonly .mdc-text-field--outlined .mdc-notched-outline__notch,
.mat-mdc-form-field.spa-readonly .mdc-text-field--outlined .mdc-notched-outline__trailing {
  border-color: rgba(0, 0, 0, 0.12) !important; /* faded outline like a disabled outlined field */
}
.mat-mdc-form-field.spa-readonly .mat-mdc-input-element,
.mat-mdc-form-field.spa-readonly textarea.mat-mdc-input-element {
  color: rgba(0, 0, 0, 0.55) !important; /* dimmed value text — still legible but clearly not editable */
  cursor: default;
}
.mat-mdc-form-field.spa-readonly .mat-mdc-floating-label,
.mat-mdc-form-field.spa-readonly .mdc-floating-label {
  color: rgba(0, 0, 0, 0.38) !important; /* MDC disabled label color */
}
.mat-mdc-form-field.spa-readonly .mat-mdc-form-field-focus-overlay {
  display: none; /* no hover/focus highlight — the field should not invite interaction */
}
.mat-mdc-form-field.spa-readonly .mdc-line-ripple::before,
.mat-mdc-form-field.spa-readonly .mdc-line-ripple::after {
  border-bottom-style: dotted; /* dotted underline mirrors MDC's disabled filled-field underline */
  border-bottom-color: rgba(0, 0, 0, 0.38) !important;
}
/* Dim prefix/suffix content (currency symbols, etc.) but keep info/copy icons usable */
.mat-mdc-form-field.spa-readonly .mat-mdc-form-field-text-prefix,
.mat-mdc-form-field.spa-readonly [matPrefix] {
  color: rgba(0, 0, 0, 0.38);
}
/* Chip inputs (text-multi/email): gray out chips in readonly mode */
.mat-mdc-form-field.spa-readonly .mat-mdc-chip {
  opacity: 0.65;
}
/* Rich-text editor: gray out the editing surface when readonly */
.spa-editor-wrap.spa-readonly .angular-editor-textarea {
  background-color: rgba(0, 0, 0, 0.06) !important;
  color: rgba(0, 0, 0, 0.55) !important;
  cursor: default;
}
.spa-editor-wrap.spa-readonly .angular-editor-toolbar {
  opacity: 0.5;
  pointer-events: none; /* toolbar buttons are meaningless when the surface is not editable */
}
.spa-editor-wrap.spa-readonly .spa-editor-label label {
  color: rgba(0, 0, 0, 0.38);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════════════
   MODERN DIALOGS — opt-in via appConfig.dialogStyle = 'modern'
   ══════════════════════════════════════════════════════════════════════════════════════════════════════
   Dialogs add the `tin-dialog-modern` panel class to their own overlay pane when the app opts in. These
   rules live here, globally, because the Material dialog SURFACE is an ancestor of every dialog component's
   view — component CSS can style downwards but never up to its own container.

   Design intent: this is a dense line-of-business app, so "modern" means a calmer, more deliberate surface —
   softer corners, layered depth instead of a flat drop shadow, a clear header band, and dialog actions that
   read as a footer rather than as loose buttons. It stays inside Material's language; the point is to make
   the app feel considered, not to make dialogs look like they came from a different product.
   Nothing below applies unless the panel class is present, so default apps are untouched. */

/* Band colour is a VARIABLE, not a hard-coded grey, so an app can retheme every dialog band from one place:
     :root { --tin-dlg-band: #ede9f6; --tin-dlg-band-text: rgba(16,24,40,0.92); }

   The default is a faintly violet neutral rather than the blue-grey used almost everywhere else in the shell
   (page background, table rows, hover states). Those were all near-neighbours, so the band read as "more of
   the same surface" instead of as chrome — at this level of subtlety it has to differ in HUE, not just in
   lightness, to register at all.

   It is deliberately SOLID. A translucent band lets table rows and text show through, and the header becomes
   unreadable over busy content.

   On following the navigation colour: that is one line — set --tin-dlg-band to your navColor. But navColor is
   frequently near-black (test-spa uses rgba(0,0,0,0.78)), and a dark band with dark text is unreadable, so
   --tin-dlg-band-text exists to be flipped to white in that case. It is NOT derived automatically, because
   silently inverting text based on a colour an app set for a different purpose is how you end up with a
   dialog nobody can read on exactly one deployment. */
:root {
  --tin-dlg-band: #f2f1f7;
  --tin-dlg-band-border: rgba(31, 27, 61, 0.13);
  --tin-dlg-band-text: rgba(16, 24, 40, 0.92);
  --tin-dlg-band-muted: rgba(16, 24, 40, 0.55);
}

.tin-dialog-modern .mat-mdc-dialog-surface {
  border-radius: 14px;
  /* Two layered shadows rather than one: a tight contact shadow to seat the dialog, and a wide soft one for
     depth. A single large blur reads as haze and makes the edge look undefined against a busy table. */
  box-shadow:
    0 1px 2px rgba(16, 24, 40, 0.10),
    0 12px 32px -8px rgba(16, 24, 40, 0.28) !important;
  overflow: hidden; /* lets the header band and footer meet the rounded corners cleanly */
}

/* Header band — separates "what this dialog is" from its content without a heavy rule. */
.tin-dialog-modern .tin-dlg-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  /* A flat tint rather than a gradient. The gradient was too faint to read as a band at all, which left the
     dialog looking exactly as plain as before — the whole point of the band is that the eye can tell where the
     chrome ends and the record begins. */
  background: var(--tin-dlg-band);
  border-bottom: 1px solid var(--tin-dlg-band-border);
}

.tin-dialog-modern .tin-dlg-title {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--tin-dlg-band-text);
  line-height: 1.3;
}

/* NOTE: the .tin-dlg-sub subtitle was REMOVED from the details dialog on request — the record reference under
   the title was noise rather than information. The variable --tin-dlg-band-muted remains for any future
   secondary text inside a band; do not reintroduce a subtitle without being asked. */

/* The header's action icons (edit, refresh, close) are chrome, not content — grouping them on a subtle
   surface stops them competing with the title for attention and gives the band a right-hand anchor. */
.tin-dialog-modern .tin-dlg-head .mat-mdc-icon-button {
  width: 36px;
  height: 36px;
  padding: 0;
}

/* Footer — a seated action bar. Buttons stay Material; the band is what makes them read as a footer. */
.tin-dialog-modern .mat-mdc-dialog-actions {
  padding: 12px 20px;
  background: var(--tin-dlg-band);
  border-top: 1px solid var(--tin-dlg-band-border);
  margin: 0;
  min-height: unset;
}

.tin-dialog-modern .mat-mdc-dialog-content {
  padding-top: 16px;
  padding-bottom: 16px;
}

/* Tabs inside a modern dialog: a lighter, tighter bar. The default Material tab header is tall enough to
   dominate a dialog whose whole purpose is the form underneath it. */
/* Tinted so the tab strip reads as a divider between the record's own fields above and its related lists
   below. On a plain white surface the tabs float, and a long form runs into its child tables with nothing
   marking the boundary — which is most of why this dialog reads as "plain". */
.tin-dialog-modern .mat-mdc-tab-header {
  background: var(--tin-dlg-band);
  border-top: 1px solid var(--tin-dlg-band-border);
  border-bottom: 1px solid var(--tin-dlg-band-border);
  --mat-tab-header-divider-height: 0;
}

.tin-dialog-modern .mat-mdc-tab .mdc-tab__text-label {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

@media (max-width: 480px) {
  .tin-dialog-modern .mat-mdc-dialog-surface { border-radius: 12px; }
  .tin-dialog-modern .tin-dlg-head { padding: 12px 16px; }
  .tin-dialog-modern .mat-mdc-dialog-actions { padding: 10px 16px; }
}

/* ---------------------------------------------------------------------------
   QUIET LOADING — SKELETON FOUNDATION

   Shared placeholder primitives for the opt-in Quiet Loading style: table ghost
   rows, tile value chips and the details-dialog form skeleton all build out of
   these two classes so they shimmer as one system rather than three.

   Colours are color-mix over the Material 3 system tokens, never hardcoded
   greys, so a skeleton themes itself in light, dark and any branded palette.
   Heights reuse --tin-field-band (declared with the form-field alignment rules
   above) so a form skeleton occupies exactly the space the real control band
   will, and nothing jumps when the data arrives.
   --------------------------------------------------------------------------- */
:root {
  --tin-skel-base: color-mix(in srgb, var(--mat-sys-on-surface, #000) 8%, transparent);   /* resting band */
  --tin-skel-sheen: color-mix(in srgb, var(--mat-sys-on-surface, #000) 14%, transparent); /* moving highlight */
  --tin-skel-radius: 4px;
  --tin-skel-speed: 1.4s;
}

/* Base chip. The 200% background is what the sweep translates across; without it the
   gradient has nowhere to travel and the animation renders as a static band. */
.tin-skel {
  display: block;
  border-radius: var(--tin-skel-radius);
  background-color: var(--tin-skel-base);
  background-image: linear-gradient(90deg, transparent 0%, var(--tin-skel-sheen) 50%, transparent 100%);
  background-size: 200% 100%;
  background-repeat: no-repeat;
  background-position: -100% 0;
  animation: tin-skel-sweep var(--tin-skel-speed) ease-in-out infinite;
  /* a skeleton is decoration around content that has not arrived — never selectable or interactive */
  user-select: none;
  pointer-events: none;
}

/* Inline variant for text-shaped placeholders (labels, captions, tile values):
   sits on the text baseline and takes its height from the surrounding font size. */
.tin-skel-text {
  display: inline-block;
  height: 1em;
  vertical-align: middle;
  border-radius: var(--tin-skel-radius);
}

@keyframes tin-skel-sweep {
  from { background-position: -100% 0; }
  to   { background-position: 100% 0; }
}

/* Reduced motion: the placeholder still communicates "content is coming" by occupying
   the space, but nothing moves. The sheen is dropped rather than paused so the chip
   does not freeze mid-sweep with a bright band stuck across it. */
@media (prefers-reduced-motion: reduce) {
  .tin-skel {
    animation: none;
    background-image: none;
  }
}

/* ---------------------------------------------------------------------------
   QUIET LOADING — TABLE STAGE (PHASE 2, FSD D3/D4)

   Two loading states for a table, and only ever one of them at a time:

     .tin-load-stage  first load, nothing on screen yet — an eased progress bar
                      with a counting percentage, a caption, and ghost rows that
                      hold the exact space the real rows will fill.
     .tin-load-line   refresh, rows already on screen — a 2px line under the
                      header and a spinning refresh icon, nothing else. Rows
                      are never dimmed or covered; they stay live.

   Lives here rather than in table.component.css because .tin-spin is shared
   with the filter component (which owns the refresh button) and the palette
   has to match the skeleton primitives declared directly above.
   --------------------------------------------------------------------------- */

.tin-load-stage {
  padding: 8px 0 4px 0;
}

/* Progress module — bar and percentage on one baseline */
.tin-load-progress {
  display: flex;
  align-items: center;
  gap: 12px;
}

.tin-load-bar {
  position: relative;
  flex: 1 1 auto;
  height: 3px;
  border-radius: 999px;
  background-color: var(--tin-skel-base);
  overflow: hidden;
}

/* The fill is driven from the component (width %, PerceivedProgress curve). The transition only smooths the
   gap between animation frames — the easing itself is in the curve, not here. */
.tin-load-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  display: block;
  width: 0;
  border-radius: 999px;
  background-color: var(--mat-sys-primary, #1976d2);
  transition: width 120ms linear;
}

/* Soft sheen travelling along the filled portion, so a stalled backend still looks alive */
.tin-load-bar-fill::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--mat-sys-on-primary, #fff) 45%, transparent) 50%, transparent 100%);
  background-size: 200% 100%;
  animation: tin-skel-sweep var(--tin-skel-speed) ease-in-out infinite;
}

/* Tabular numerals: without them the width of the digits changes as the value counts up and the caption jitters. */
.tin-load-percent {
  flex: 0 0 auto;
  min-width: 4ch;
  text-align: right;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  color: color-mix(in srgb, var(--mat-sys-on-surface, #000) 60%, transparent);
}

.tin-load-caption {
  margin-top: 6px;
  font-size: 12px;
  color: color-mix(in srgb, var(--mat-sys-on-surface, #000) 55%, transparent);
}

/* Ghost rows — one cell per visible column, so the stage is the same shape as the table that replaces it */
.tin-load-ghosts {
  margin-top: 12px;
}

.tin-load-ghost-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 10px;
  height: var(--tin-field-band, 40px);
  border-bottom: 1px solid color-mix(in srgb, var(--mat-sys-on-surface, #000) 6%, transparent);
}

.tin-load-ghost-cell {
  flex: 1 1 0;
  min-width: 0;
}

.tin-load-ghost-cell .tin-skel {
  height: 12px;
}

/* Refresh: a 2px line flush under the table header. Deliberately thinner and quieter than the initial bar —
   the user already has content to read, this is peripheral. */
.tin-load-line {
  position: relative;
  height: 2px;
  margin-bottom: 4px;
  background-color: var(--tin-skel-base);
  overflow: hidden;
}

.tin-load-line-fill {
  position: absolute;
  inset: 0 auto 0 0;
  display: block;
  width: 0;
  background-color: color-mix(in srgb, var(--mat-sys-primary, #1976d2) 70%, transparent);
  transition: width 120ms linear;
}

/* Hand-off: the real rows arrive where the ghosts were, so they fade and settle rather than snapping in. The
   animation fires once, when the table element is created — a refresh never re-runs it because the element is
   already in the DOM (D4: rows are never re-mounted on refresh). */
.tin-load-in {
  animation: tin-load-fade-in 180ms ease-out;
}

@keyframes tin-load-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* Shared spin, applied to the header refresh icon while a quiet refresh is in flight */
.tin-spin {
  animation: tin-spin-rotate 1.2s linear infinite;
}

@keyframes tin-spin-rotate {
  to { transform: rotate(360deg); }
}

/* Reduced motion: the stage still reports progress (the bar grows, the percentage counts — both are value
   changes, not motion), but nothing sweeps, pulses or rotates. */
@media (prefers-reduced-motion: reduce) {
  .tin-load-bar-fill::after {
    animation: none;
    background-image: none;
  }

  .tin-spin,
  .tin-load-in {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
   QUIET LOADING — ghost furniture for the NON-table view types.

   The table's ghost rows are column-shaped, which is exactly wrong for a card
   grid or a capsule strip: a placeholder should promise the layout that is
   about to arrive, and a row of cells promises a table. Each view therefore
   gets its own shape. They all reuse the shared .tin-skel shimmer, so there is
   one animation and one palette across every surface.
   --------------------------------------------------------------------------- */

/* Capsules — a wrapping strip of pills. Varied widths so it reads as labels
   rather than as a progress meter; deterministic, so it looks the same each time. */
.tin-load-ghost-capsules {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}

.tin-load-ghost-capsule {
  height: 32px;
  width: 96px;
  border-radius: 16px;
}

.tin-load-ghost-capsules .tin-load-ghost-capsule:nth-child(2) { width: 132px; }
.tin-load-ghost-capsules .tin-load-ghost-capsule:nth-child(3) { width: 78px; }
.tin-load-ghost-capsules .tin-load-ghost-capsule:nth-child(4) { width: 116px; }
.tin-load-ghost-capsules .tin-load-ghost-capsule:nth-child(5) { width: 88px; }

/* Cards — the same auto-fit grid the real card view uses, so the ghosts occupy
   the space the cards will land in rather than reflowing when they arrive. */
.tin-load-ghost-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: 16px;
  margin-top: 14px;
}

.tin-load-ghost-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--mat-sys-on-surface, #000) 8%, transparent);
}

.tin-load-ghost-card-title {
  height: 1.15em;
  width: 55%;
}

.tin-load-ghost-card-short {
  width: 40%;
}

/* Grouped — a heading bar over a few rows, twice. Mirrors the group-card header
   whose alignment was fixed in WS-8, so the stage lines up with what replaces it. */
.tin-load-ghost-groups {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 14px;
}

.tin-load-ghost-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--mat-sys-on-surface, #000) 8%, transparent);
}

.tin-load-ghost-group-head {
  height: 20px;
  width: 34%;
  margin-bottom: 4px;
}

.tin-load-ghost-group-row {
  width: 100%;
}

.tin-load-ghost-groups .tin-load-ghost-group-row:last-child { width: 62%; }

/* Grouped ghosts sit on the same white card surface the real group cards use — an outline alone read as a
   flat bar against the page tint and did not look like a card at all. */
.tin-load-ghost-group {
  background: var(--mat-sys-surface, #fff);
  box-shadow: 0 1px 3px color-mix(in srgb, var(--mat-sys-on-surface, #000) 10%, transparent);
  border-color: transparent;
}

.tin-load-ghost-group-items {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

/* Card ghosts get the same surface treatment as the grouped ones, and for the same reason: a 8%-opacity
   outline on a tinted page reads as a flat band, not as a card. The real card view is a white raised
   surface, so the placeholder should be too. */
.tin-load-ghost-card {
  background: var(--mat-sys-surface, #fff);
  box-shadow: 0 1px 3px color-mix(in srgb, var(--mat-sys-on-surface, #000) 10%, transparent);
  border-color: transparent;
}
