/* ==========================================================================
   AXING WEP — app-specific styles
   --------------------------------------------------------------------------
   Layered on top of bootstrap.css → icons.css → axing.custom.css. The
   templates use Bootstrap components (.card, .alert, .list-group, .badge)
   and utilities (.fs-6, .text-secondary, .d-flex, .gap-*, .mb-*) for all
   layout, sizing and spacing. This file only carries rules that have no
   utility / component equivalent.
   ========================================================================== */


/* --- Horizontal-overflow clip ------------------------------------------- */
/* `overflow-x: clip` on <body> silently swallows the few pixels of right-
   side overflow that the full-bleed divider's `calc(50% - 50vw)` produces
   on Windows whenever a vertical scrollbar appears (50vw counts the
   scrollbar gutter, 50% doesn't). Without this, the page would gain a
   horizontal scrollbar the moment the vertical one shows up.
   We deliberately do NOT set `scrollbar-gutter: stable` here — that would
   permanently reserve the scrollbar gutter even on short pages with no
   scrollbar, leaving an empty strip on the right where the header,
   divider and status bar visually stop short. The cost: when the page
   grows long enough to need a scrollbar, the layout briefly jumps left
   by the gutter width. That's the standard browser behaviour and feels
   less wrong than a permanently truncated header.
   `clip` (not `hidden`) because `hidden` would turn <body> into a scroll
   container and break `position: sticky` on the status bar. */
body { overflow-x: clip; }


/* --- Container hard-cap at 960 px --------------------------------------- */
/* Bootstrap's .container-lg keeps ramping up at xl (1140 px) and xxl
   (1320 px) breakpoints. We don't want that — at 4K reading distance in
   the workshop, lines get too long. Pin it to 960 px from xl up. The
   selector matches Bootstrap's specificity (0,1,0) and this stylesheet
   loads after bootstrap.css, so cascade order is the tie-breaker. */
@media (min-width: 1200px) {
  .container-lg {
    max-width: 960px;
  }
}


/* --- Disabled-button visibility in dark theme --------------------------- */
/* axing.custom.css applies a rest-state `opacity: 0.95` to filled and
   outline buttons in dark theme (its "dim default → bright on hover"
   pattern). That rule has the same specificity (0,2,0) as Bootstrap's
   `.btn:disabled { opacity: var(--bs-btn-disabled-opacity) }` and sits
   later in the cascade, so a disabled button keeps the 0.95 opacity
   instead of dropping to 0.65 — visually almost indistinguishable from
   the enabled state.
   Re-assert Bootstrap's disabled-opacity here with one extra qualifier
   to bump specificity to (0,2,1), so :disabled wins cleanly. Light
   theme isn't affected (axing has no rest-state opacity there). */
[data-bs-theme="dark"] .btn:disabled,
[data-bs-theme="dark"] .btn.disabled,
[data-bs-theme="dark"] fieldset:disabled .btn {
  opacity: var(--bs-btn-disabled-opacity);
}


/* --- App header & sticky status bar ------------------------------------- */
/* Sticky strategy: only the product status bar sticks; the top header
   scrolls away with the page. That keeps a single sticky layer (no more
   header-vs-status-bar pixel-snapping seams) and gives the inspector
   more vertical reading area once they're past the product picker.
   The header keeps `padding-top: env(safe-area-inset-top)` so on
   notched iPhones the logo/buttons don't sit behind the notch — on
   desktop the env() resolves to 0 and the rule is a no-op. */
body > header {
  padding-top: env(safe-area-inset-top);
}
.ps-status-bar {
  position: sticky;
  top: 0;
  z-index: 1019;
}


/* --- [hidden] reliability ------------------------------------------------ */
/* Bootstrap reboot has `[hidden] { display: none !important; }`, but its
   utility classes (.d-flex, .d-block, .d-grid …) come LATER in the same
   stylesheet with the same specificity (0,1,0) and the same !important —
   so an element with both `hidden` attribute and a .d-* utility loses
   the cascade to the utility, stays visible. Re-declaring [hidden] here
   in app.css (loaded after bootstrap.css) makes it last → it wins.
   Without this, e.g. `<button class="d-flex" hidden>` would still render. */
[hidden] {
  display: none !important;
}


/* --- Header three-column layout ----------------------------------------- */
/* Brand left, app title perfectly centered, action buttons right. Bootstrap's
   .navbar > .container-* defaults to flex with justify-content: space-between,
   which only positions items between left and right edges — the middle child
   sits in the middle of remaining space, NOT at the viewport center.
   Grid 1fr/auto/1fr makes the title cell take only its content width while
   the side columns absorb equal space, putting the title at the true center.
   Bootstrap's align-items: center carries over and applies to grid items
   (same semantics). */
/* .navbar > .navbar-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
} */


/* --- Header app title ---------------------------------------------------- */
/* Truncation done by Bootstrap's .text-truncate utility (applied in markup);
   only max-width has no Bootstrap counterpart. */
.navbar-app-title {
  max-width: 20rem;
}


/* --- Typauswahl product list -------------------------------------------- */
/* Cap the list height so it never pushes the rest of the page off-screen
   on tall product catalogs (currently 640 entries). overflow-y: auto +
   list-group-flush gives a scroll container with native row separators. */
.ps-product-list {
  max-height: clamp(12rem, 25vh, 20rem);
  overflow-y: auto;
}


/* --- Sticky-layer-2 progress min-width ---------------------------------- */
/* Reserve enough room for the progress label + bar; without min-width the
   typ/name column would push the bar to ~0 px on long product names. */
.ps-status-bar-progress {
  min-width: 11rem;
}


/* --- Full-bleed section divider ----------------------------------------- */
/* The <hr>s between Typauswahl / Prüfschritte / Abschluss live inside
   <main class="container-lg">, but visually they should span the entire
   viewport — including the area outside the centered container on wide
   screens. Standard "full-bleed" trick: negative inline margins equal to
   half the difference between the parent's content width (50%) and the
   viewport width (50vw) stretch the element to ~100vw without setting an
   explicit width.
   Note: `50vw` still includes the vertical scrollbar gutter on Windows,
   so the calc can produce a few pixels of overflow on the right. The
   `overflow-x: clip` rule on <body> at the top of this file silently
   swallows that — without it, the divider would trigger a horizontal
   scrollbar the moment a vertical one appears. */
.ps-section-divider {
  margin-left:  calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}


/* --- PS list inter-card gap --------------------------------------------- */
/* Bootstrap's gap-* utilities skip 2rem (gap-4 = 1.5rem, gap-5 = 3rem),
   so the PS list takes its gap from here instead of a utility class. */
.ps-list {
  gap: 2rem;
}


/* --- Status icon-buttons (PS-card header) ------------------------------- */
/* The header status buttons are icon-only and need to be square — Bootstrap
   has no built-in icon-button utility set, so the layout (square box,
   centred icon, no padding) lives here. Three states use Bootstrap's
   outline variants directly: btn-outline-success, -danger, -secondary. */
.ps-card-status .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
}


/* --- N/A icon (custom Tabler-style dash) -------------------------------- */
/* No matching dash/minus icon in css/icons.css (which is part of the
   AXING template and not modified here). Project-local additions follow
   the same masked-currentColor pattern (see icons.css comment), so a
   <span class="ico ico-20 ico-dash"> picks up the parent's text colour. */
.ico-dash {
  --ico: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12l14 0' /></svg>");
}


/* --- PS card status indicator ------------------------------------------- */
/* The whole card surface tints with the status — Bootstrap's *-bg-subtle
   tokens are theme-aware (soft tint in light, darkened wash in dark) and
   exist exactly for this purpose. We overload --bs-card-bg; the cap-bg
   override below pins .card-header/.card-footer to that same value so
   header/body/footer read as one continuous tinted surface, no internal
   stripes. The matching outer border uses the saturated --bs-{status}
   token for definition. */
.ps-card {
  --bs-card-cap-bg: var(--bs-card-bg);
}
.ps-card > .card-header { border-bottom: 0; }
.ps-card > .card-footer { border-top:    0; }
.ps-card[data-status="passed"] {
  --bs-card-border-color: var(--bs-success);
  --bs-card-bg:           var(--bs-success-bg-subtle);
}
.ps-card[data-status="failed"] {
  --bs-card-border-color: var(--bs-danger);
  --bs-card-bg:           var(--bs-danger-bg-subtle);
}
.ps-card[data-status="na"] {
  --bs-card-border-color: var(--bs-secondary);
  --bs-card-bg:           var(--bs-secondary-bg-subtle);
}
/* Failed-state: highlight the required-message field */
.ps-card[data-status="failed"] .ps-card-message {
  border-color: var(--bs-danger);
}


/* --- Freitext / merkmal line breaks ------------------------------------- */
/* PIM Freiprüftext values use \n. Many frequency-dependent merkmal
   values (Rückflussdämpfung, Durchgangsdämpfung, Abzweigdämpfung etc.)
   also ship as multi-line strings with one "value @ frequency-band" per
   line. white-space: pre-line is the only way to honour them in HTML —
   no Bootstrap utility for it. The merkmal-card's flex row uses
   align-items-baseline, which anchors the "SOLL ▸" prefix at the first
   line of the value; subsequent lines stack right below it. */
.ps-freitext,
.ps-merkmal-value {
  white-space: pre-line;
}


/* --- File-row filename trigger ------------------------------------------ */
/* The filename in a file-list-row (PS-foto upload, Closing attachments)
   is a button that opens the image-preview modal. Default state is body
   text; hover/focus shows a link affordance in brand teal. Bootstrap's
   .btn-link defaults to the link colour (already teal here), so resetting
   it back to body text on a button-styled-as-link costs the same custom
   CSS either way — keep it explicit. */
.ps-file-name:hover,
.ps-file-name:focus-visible {
  color: var(--ax);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}


/* --- Read-only signature image ------------------------------------------ */
/* Cap the rendered signature width on wide screens so it doesn't blow up
   to whole-page proportions in the read-only viewer. The pad's intrinsic
   bitmap is 400×150; we let it scale down via .w-100 (in markup) and cap
   at 25 rem on bigger viewports. No Bootstrap utility for max-width
   in rem units. */
.ps-readonly-signature-img {
  max-width: 25rem;
}


/* --- Image preview ------------------------------------------------------- */
/* max-height tied to the viewport keeps tall photos from overflowing.
   No Bootstrap utility for max-height in dvh units. */
#previewModal .preview-img {
  max-height: calc(100dvh - 10rem);
  object-fit: contain;
}


/* --- Signature pad ------------------------------------------------------- */
/* Pointer Events on a canvas need touch-action:none, otherwise mobile
   browsers eat the gesture as a scroll/pinch and signature.js never sees
   the pointermove. Crosshair cursor signals the "writing target" on
   desktop.

   Responsive sizing: the canvas keeps its 400×150 *intrinsic* bitmap via
   the width/height attributes, but on screens narrower than ~25rem we let
   it shrink to fit the container (with aspect-ratio so it scales
   proportionally). signature.js' setupBitmap() reads clientWidth/Height
   on init and sets the bitmap to the actual CSS size × DPR, so the
   strokes stay crisp at whatever final size the canvas ends up at. */
.ps-signature {
  touch-action: none;
  cursor: crosshair;
  width: 100%;
  max-width: 25rem;
  height: auto;
  aspect-ratio: 400 / 150;
}


/* --- History-table result-badge alignment ------------------------------- */
/* Bootstrap's .badge defaults are display: inline-block + vertical-align:
   baseline. In the history-table cell (no surrounding flex context, only
   the table's `align-middle`) that combination drifts: the inline-block
   box's baseline sits well below the line-box centre, so the badge ends
   up offset from the centre even when align-middle is set on the cell.
   Forcing display: inline + vertical-align: middle makes the badge flow
   like text and ride the line-box centre — same visual result as the
   user's manual DevTools edit. Scoped strictly to the history-table
   badge so other badges (cards, read-only header) aren't affected. */
.ps-history-entry > td > .ps-history-result-badge {
  display: inline;
  vertical-align: middle;
}


/* --- Mode-toggle: edit vs. read-only viewer ----------------------------- */
/* The page has two top-level visibility states driven by [data-mode] on
   <body> (managed by history.js):
   - "edit" (default): Typauswahl / Prüfschritte / Abschluss visible,
     #section-readonly hidden.
   - "readonly": only #section-readonly visible — the edit sections AND
     the sticky status bar are hidden so the read-only view renders on
     a clean canvas.
   The edit-mode DOM stays in place during read-only viewing, so any
   in-flight inspection state survives a brief detour into history. */
body:not([data-mode="readonly"]) #section-readonly {
  display: none !important;
}
body[data-mode="readonly"] :is(
  #section-typauswahl,
  #section-pruefschritte,
  #section-abschluss,
  #ps-status-bar,
  .ps-section-divider
) {
  display: none !important;
}


/* --- Validation feedback (extends Bootstrap .is-invalid) ----------------- */
/* Bootstrap's .is-invalid only styles .form-control / .form-check elements
   out of the box. validation.js applies the same class to two additional
   element types — PS-cards (no missing-status) and the signature canvas
   (empty draw pad) — so we extend the rule here.

   Order matters: these rules must come AFTER the .ps-card[data-status="*"]
   tints above, because both selectors have specificity (0,2,0) and we want
   .is-invalid to win when both are set (e.g. a PS marked "passed" that
   somehow also matches a validation error — defensive). */
.ps-card.is-invalid,
.ps-signature.is-invalid {
  box-shadow: 0 0 0 0.2rem rgba(var(--bs-danger-rgb), 0.2);
}
.ps-card.is-invalid {
  --bs-card-border-color: var(--bs-danger);
}
.ps-signature.is-invalid {
  border-color: var(--bs-danger) !important;
}
