
/* Added: Leaflet's core stylesheet, vendored as a sibling asset and imported RELATIVELY.
   spa-map switched from Google Maps to Leaflet + OpenStreetMap (owner directive 2026-08-19), and Leaflet
   cannot position its panes or tiles without this. It lives outside map.component.css because Angular's
   emulated view encapsulation cannot reach elements Leaflet creates at runtime — the same reason the
   .spa-map-dialog panel reset is down at the bottom of this file. See leaflet-core.css for why it is
   vendored rather than @imported from node_modules. Must stay the FIRST rule-bearing line: CSS @import
   is only legal before other rules. */
@import './leaflet-core.css';

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

/* Added: exception for the table header's grouped data actions (download / upload / import), in the same
   shape as the .suffix-icons exception below and the spa-groups chip exception above.
   WHY IT IS NEEDED: the `.tin-row .mat-mdc-icon-button` rule at :43 is a 14px !important blanket meant for
   the tiny icons in option components. The header's action buttons happen to live in a .tin-row, so they
   were being crushed to 14px — which is what made them read as unprofessional. The component's own
   stylesheet cannot win that fight (a component rule has no !important and loses to this one), so the
   size has to be stated here, next to the rule it is excepting.
   34px is deliberate: 34 + 2*2 pill padding + 2*1 pill border = 40px, matching the refresh icon button
   beside it, so the two line up on the row. */
.tin-row .tin-header-actions .mat-mdc-icon-button,
.tin-row .tin-header-actions .mat-icon-button,
.tin-header-actions .mat-mdc-icon-button,
.tin-header-actions .mat-icon-button {
  width: 34px !important;
  height: 34px !important;
  padding: 0 !important;
  margin: 0 !important;
  line-height: 34px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
}

.tin-row .tin-header-actions .mat-mdc-icon-button .mat-icon,
.tin-row .tin-header-actions .mat-icon-button .mat-icon,
.tin-header-actions .mat-mdc-icon-button .mat-icon,
.tin-header-actions .mat-icon-button .mat-icon {
  font-size: 19px !important;
  width: 19px !important;
  height: 19px !important;
  line-height: 19px !important;
  margin: 0 !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;
  /* Changed: minmax(0, 1fr) instead of a bare 1fr. `1fr` is shorthand for `minmax(auto, 1fr)`, and that
     `auto` floor resolves to the grid item's MIN-CONTENT width. So a child that cannot shrink below its
     content — a Material field with a long label, a hint, or a suffix icon — forces its own track wider than
     half the grid and pushes the whole container open, overflowing whatever card or dialog holds it.
     `.tin-grid > * { width: 100% }` below does NOT save you: width sizes the item TO the track, it never
     lowers the track's minimum. minmax(0, 1fr) sets that floor to 0, so the two columns stay equal at any
     width and the grid never exceeds its container. Do not 'simplify' this back to `1fr 1fr`. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 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: var(--tin-field-subscript-reserve) !important; /* Changed: same 5px as before, now read from the variable so FORM-1/FORM-3 below can reserve the IDENTICAL amount under a non-field row */
}

/* Form hints are ITALIC — owner, 2026-08-19, looking at the Add Pig dialog's Stage and Date of Birth fields:
   "the floating action is almost the same as hints… that might just be a little bit of a sore, and maybe if
   it's in italics, it's clear that this is a hint… still the grayish, because it should also be clear that
   this is just extra information, optional information."

   A floated Material label and a hint sit a few pixels apart, both small and both grey, so the only thing
   separating "what this field is" from "advice about filling it in" was position. Slant separates them at a
   glance without adding a second colour or another size to the form.

   Only the slant changes. The measured colour (rgb(68,71,78)) and size (12px) are deliberately NOT touched —
   he asked for the same grey, and 12px is already the small end of legible.

   Scoped to spa-form so this stays a FORM convention: hints also appear in page filters and standalone field
   components outside forms, and those are not what he was looking at.

   Does not affect the collapsed-subscript behaviour: subscriptSizing="dynamic" measures the rendered line and
   an italic line has the same height, so a field with no hint still reserves 0px (see the FORM-2 note below). */
spa-form .mat-mdc-form-field-hint {
  font-style: italic;
}

/* ---------------------------------------------------------------------------
   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 */
  --tin-field-subscript-reserve: 5px; /* Added: the margin a mat-form-field reserves BELOW its control band, making its outer row 45px. Named so the band and this reserve are one pair of numbers instead of two literals that can drift apart */
}

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

/* FORM-4 — match the field's OUTER ROW, not just its control band.

   FORM-1/FORM-3 above centre spa-label on the 40px band, which is why caption and
   value line up with the input beside them. But a mat-form-field's outer row is
   45px: the 40px band PLUS the 5px its subscript wrapper reserves underneath
   (.mat-mdc-form-field-subscript-wrapper, above). spa-label stopped at 40px, so a
   form alternated 55px / 50px / 55px row pitch — measured on piglet's Record
   Feeding dialog at 1920x1080, and the source of the reported "awkward spaces".

   Reserved as padding BELOW a still-40px band rather than by raising min-height to
   45px. That distinction is load-bearing: this host is align-items:center, so a
   45px min-height would centre the text in 45px and drop the caption 2.5px below
   the input it is paired with — re-breaking FORM-1/FORM-3's centring to fix the
   pitch. A field's box is top-aligned in its row with the reserve underneath, so
   the label must be built the same way round.

   Same spa-form scope and the same reason: profile and tenant-settings render
   spa-label outside a form, where trailing space would change inline flow.

   Changed: spa-check JOINS this rule. label-comp-1 left it out as plausible but
   unmeasured; it has now been measured on test-spa :4200 at 1920x1080 and the gap
   is real and identical in kind:

     Notif Config > Create      spa-check "Enable Notifications"  h=40.00 top=489.55
                                mat-form-field "Entity"           h=45.00 top=539.55
                                -> row pitch 50px against the 55px standard

     Customer > Create          mat-form-field "VAT Number"/"TIN"  h=65.00 (hinted)
                                spa-check "Requires a PO number"   h=44.19 (hinted)

   Both checkbox shapes want the same 5px, for the same reason a field always
   carries it: a mat-form-field reserves it whether or not a hint is showing
   (45px bare, 65px = 40 band + 20 subscript + 5 reserve). A checkbox that
   reserves nothing is the odd row out in either shape.

   box-sizing: content-box is what makes this work and is NOT cosmetic here. The
   host is border-box under the global reset, so min-height:40px would SWALLOW the
   padding: content 24px + 5px padding still measures 40px total, the row stays
   40px, AND the checkbox re-centres in a 35px content area — 2.5px above the
   input beside it. Under content-box the 40px floor applies to the CONTENT box,
   so the checkbox keeps its 20px centreline and the 5px lands underneath.

   The hinted checkbox is already taller than the 40px floor (44.19px), so it has
   no slack to centre and simply gains the 5px beneath — 49.19px. That is the
   intended outcome, not an overshoot.

   Deliberately NOT touched: check.component.css's 24px state layer (the f041f7a-era
   fix for the dead 8px at each end of a 40px layer). This rule adds padding to the
   HOST and changes no Material density token, so the layer stays 24px and the
   .mat-mdc-checkbox-touch-target stays 48x48 — it is absolutely positioned and does
   not participate in layout, so no touch target shrinks. Verified live, both shapes. */
spa-form spa-label,
spa-form spa-check {
  box-sizing: content-box;
  padding-bottom: var(--tin-field-subscript-reserve);
}

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

/* ===========================================================================================
   THE DISCLOSURE MARKER — added 2026-08-17 (owner: "the person looks from the left").
   A collapsed section header used to say it was expandable with one small chevron at the far RIGHT edge.
   The eye starts on the LEFT, so a shut group read as flat text running down the page with nothing inviting
   anyone to open it — the owner's words, about the Getting Started module bands, and true of every band in
   the library that folds the same way.
   This is the one definition of that affordance. It lives in the GLOBAL stylesheet rather than in any one
   component because there is no shared collapsible-header component in this library (checked 2026-08-17:
   the Day Book, the setup guide's two pickers and spa-table's sectionConfig header each draw their own),
   and four private copies of an affordance is how they drift apart. Global CSS matches component elements
   regardless of view encapsulation — only a component's OWN styles are scoped — so one rule reaches all of
   them. The class names are unique to this block, so nothing here fights an existing component rule.
   Usage:
     <button class="tin-disc-row" ...>
       <span class="tin-disc" [class.open]="!collapsed"><mat-icon>chevron_right</mat-icon></span>
       ...
   ONE glyph, rotated — never two glyphs swapped. A swap cannot animate, and the rotation is what makes the
   control feel like it opens something rather than redrawing.
   =========================================================================================== */
.tin-disc {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.05);
  color: rgba(0, 0, 0, 0.5);
  transition: background-color 0.15s ease, color 0.15s ease, transform 0.18s ease;
}

.tin-disc .mat-icon,
.tin-disc mat-icon {
  font-size: 18px;
  width: 18px;
  height: 18px;
  line-height: 18px;
}

/* Open. 90deg turns chevron_right into a chevron_down without changing the glyph. */
.tin-disc.open {
  transform: rotate(90deg);
}

/* The whole header row is the control, so the marker responds to the ROW's hover, not its own — otherwise
   the affordance only lights up once the pointer is already on the 22px square, which is the problem. */
.tin-disc-row:hover .tin-disc {
  background: rgba(21, 101, 192, 0.12);
  color: #1565c0;
}

.tin-disc-row:focus-visible .tin-disc {
  background: rgba(21, 101, 192, 0.12);
  color: #1565c0;
}

/* The marker is an affordance, not decoration: it must still point the right way with motion off. */
@media (prefers-reduced-motion: reduce) {
  .tin-disc { transition: none; }
}

/* ---------------------------------------------------------------------------
   FULL-BLEED MAP DIALOG  (.spa-map-dialog)
   Added for the map viewer revamp, 2026-08-18.

   These rules live here rather than in mapDialog.component.css because Material renders the dialog panel
   into an overlay container OUTSIDE the component's view encapsulation — a component stylesheet cannot
   reach .mat-mdc-dialog-surface at all.

   The owner's directive was that the viewer "should almost consume the screen with as few bezels as
   possible so the MAP AREA is maximized". Material's defaults work against that in three specific places,
   and each one is reset below:
     1. .mat-mdc-dialog-surface carries padding (and the container adds its own) — the map would be inset
        on all four sides by ~24px for no reason.
     2. .mat-mdc-dialog-content has a max-height of 65vh, which would cap the map regardless of how tall
        the dialog is. This dialog uses no dialog-content at all, but the reset is kept so a future
        content wrapper cannot silently reintroduce the cap.
     3. The surface does not stretch, so a height on the dialog config alone does not reach the component.
   --------------------------------------------------------------------------- */
.spa-map-dialog .mat-mdc-dialog-surface {
  padding: 0 !important;
  border-radius: 14px;
  overflow: hidden;
  background: #11141a;          /* the map's own ground — no white corners while tiles paint */
  box-shadow:
    0 1px 2px rgba(16, 24, 40, 0.12),
    0 24px 60px -12px rgba(16, 24, 40, 0.45) !important;
}

.spa-map-dialog .mat-mdc-dialog-container {
  padding: 0 !important;
  height: 100%;
}

/* The component must actually receive the full height, not just be allowed it. */
.spa-map-dialog .mat-mdc-dialog-container .mdc-dialog__container,
.spa-map-dialog .mat-mdc-dialog-container .mat-mdc-dialog-surface > * {
  height: 100%;
}

.spa-map-dialog .mat-mdc-dialog-content {
  max-height: none !important;
  padding: 0 !important;
  margin: 0 !important;
}

/* On a phone the dialog is the screen: square off the corners so there is no wasted rim at all. */
@media (max-width: 599px) {
  .spa-map-dialog .mat-mdc-dialog-surface { border-radius: 0; }
}

/* ---------------------------------------------------------------------------
   FULL-BLEED DOCUMENT VIEWER  (.spa-doc-viewer-panel)
   Added for the document viewer redesign, 2026-08-18.

   Same reason as .spa-map-dialog above: Material renders the dialog panel into an overlay container
   OUTSIDE the component's view encapsulation, so documentViewerDialog.component.css cannot reach
   .mat-mdc-dialog-surface at all.

   The acceptance criterion this exists to satisfy is measurable: the document area must exceed 90% of the
   dialog's inner width AND height. Material's defaults take that away in three places, and the third is
   the one that quietly caps everything:
     1. .mat-mdc-dialog-surface and .mat-mdc-dialog-container each carry ~24px of padding — 48px of inset
        on every side, for nothing.
     2. The surface does not stretch, so a height in the dialog config never reaches the component.
     3. .mat-mdc-dialog-content has a max-height of 65vh. This viewer uses no dialog-content, but the
        reset stays so a future content wrapper cannot silently reintroduce the cap — which is precisely
        how the component being replaced ended up with a 60vh iframe.
   --------------------------------------------------------------------------- */
.spa-doc-viewer-panel .mat-mdc-dialog-surface {
  padding: 0 !important;
  border-radius: 14px;
  overflow: hidden;
  background: #1e1f22;          /* the stage colour — no white corners while the document paints */
  box-shadow:
    0 1px 2px rgba(16, 24, 40, 0.12),
    0 24px 60px -12px rgba(16, 24, 40, 0.45) !important;
}

.spa-doc-viewer-panel .mat-mdc-dialog-container {
  padding: 0 !important;
  height: 100%;
}

.spa-doc-viewer-panel .mat-mdc-dialog-container .mdc-dialog__container,
.spa-doc-viewer-panel .mat-mdc-dialog-container .mat-mdc-dialog-surface > * {
  height: 100%;
}

.spa-doc-viewer-panel .mat-mdc-dialog-content {
  max-height: none !important;
  padding: 0 !important;
  margin: 0 !important;
}

/* On a phone the dialog is the screen: square the corners so there is no wasted rim at all. */
@media (max-width: 599px) {
  .spa-doc-viewer-panel .mat-mdc-dialog-surface { border-radius: 0; }
}
