/**
 * ProofDocs application-specific CSS overrides and custom components.
 *
 * This stylesheet extends the Falcon Bootstrap 5 theme with ProofDocs-specific
 * styles. It is loaded after theme.css and user.css in the base template.
 *
 * Accessibility notes:
 * - All animations and transitions respect prefers-reduced-motion (WCAG 2.3.3).
 * - Hover effects are gated behind @media (hover: hover) so touch devices do not
 *   get stuck in a hover state after tap (sticky-hover bug on iOS/Android).
 * - forced-colors disables decorative box-shadows and gradients so Windows High
 *   Contrast Mode can repaint affordances with system colors.
 */

/* Locked-content blur for unpaid reporting pages (JRA/JCA category cards and table rows). */
/* Replaces inline filter + user-select rules so the visual hides behind a single, auditable class. */
.pd-blur-lock {
  filter: blur(10px);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.pd-blur-lock-block {
  filter: blur(10px);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  pointer-events: none;
}

/* Lift the blur off a locked row while it holds keyboard focus. A CSS filter is */
/* applied to the whole subtree including every descendant's focus outline, and a */
/* child cannot cancel its ancestor's filter — so the one focusable element inside */
/* a locked row (the unlock link) was a tab stop whose focus ring rendered as an */
/* unreadable smear (WCAG 2.4.7). The row must therefore be unblurred as a whole. */
/* This reveals nothing withheld: the grades are masked server-side and the */
/* improvement text is not rendered at all for unpaid reports — the row holds the */
/* user's own sentence plus the unlock link. */
.pd-blur-lock:has(a:focus-visible) {
  filter: none;
}

/* Global CSS custom properties for consistent theming across the application. */
/* Brand colors live in Falcon variables (--falcon-primary, --falcon-info, --falcon-warning, etc.). */
/* This block holds twelve declarations in four groups, not one: the four */
/* --badge-*-hover tones, which a .badge cannot read from --falcon-btn-hover-bg */
/* because it sits in no .btn-* context; --light-grey and --dark-text, pinned to the */
/* light palette for the consent banner; --pd-ink-on-brand-fill plus the two */
/* --pd-shadow-sticky* and the two --pd-shadow-card-hover*, for which Falcon ships no */
/* upward-facing and no hover shadow token; and --light-bg, which is plain white and */
/* predates the rest. Eight of the twelve have nothing to do with badges - see each */
/* declaration's own comment below. The count is a premise of the reasoning above it, */
/* so it is re-counted whenever a declaration is added or removed: it said eleven */
/* while a fifth badge tone had already gone on 22.08.2026 and two card-hover shadows */
/* had arrived, which is two errors cancelling to a wrong number in the middle. */
:root {
  --light-bg: #ffffff;
  /* Both values below are Falcon's own light-theme tokens written out as literals,
     not as var(): the consent banner is deliberately pinned to the light palette
     (a third-party widget whose two buttons must stay equally legible), and
     --falcon-gray-300 / --falcon-gray-1100 flip to #344050 / #f9fafd in dark mode.
     Referencing them would silently make the banner theme-aware — a separate
     question, not a colour cleanup. Measured after the change: 13.76:1 for the
     button label, well above the 4.5:1 minimum. */
  --light-grey: #d8e2ef; /* = Falcon --falcon-gray-300 (light) */
  --dark-text: #0b1727; /* = Falcon --falcon-gray-1100 (light), the brand's "Font Dark" */

  /* Badge hover fills. These are not free values: each one is Falcon's own
     button hover shade for that variant, which the theme defines as
     --falcon-btn-hover-bg but only inside the .btn-<variant> scope — a .badge
     sits in no such scope and cannot read it, which is why the value is
     repeated here rather than referenced. Deriving it instead
     (color-mix(in srgb, var(--falcon-primary) 85%, black)) reproduces three of
     the four exactly but lands one channel off on success (178 vs 179, the
     browser rounds 178.5 down), so the literals stay. A fifth value existed for
     the secondary variant and was removed on 22.08.2026: no bg-secondary badge
     in this project sits inside an anchor, so the rule that read it never
     matched. They are pinned against
     theme.min.css by tests/test_theme_derived_values.py, so a theme upgrade
     that moves a shade cannot leave these silently behind. */
  --badge-primary-hover: #2569c3; /* .btn-primary  --falcon-btn-hover-bg */
  --badge-info-hover: #21a0d7; /* .btn-info     --falcon-btn-hover-bg */
  --badge-success-hover: #00b368; /* .btn-success  --falcon-btn-hover-bg */
  --badge-warning-hover: #d06d35; /* .btn-warning  --falcon-btn-hover-bg */

  /* Ink for text sitting on one of the light brand fills (info, warning, success).
     Deliberately a literal and NOT --falcon-gray-1100: Falcon inverts its whole gray
     scale with the theme, so gray-1100 resolves to #0b1727 in light mode but #f9fafd
     in dark mode. The three fills themselves are brand colours and stay identical in
     both themes, so an ink that follows the theme lands back on near-white and
     reproduces the very contrast failure it was introduced to remove — measured on the
     dark report page: #f9fafd on #00d27a is 1.92:1. This value is the light-mode value
     of gray-1100 and measures 9.00 / 8.32 / 6.89 on success / info / warning. */
  --pd-ink-on-brand-fill: #0b1727;

  /* Upward shadow of the two fixed bottom bars (.sticky-cta-bar on the formular
     pages, .pd-sticky-cta on the reporting pages). Falcon's four box-shadow tokens
     all cast downward, so neither bar can use one; the value was therefore written
     out — and each of the two bars carried its own light and dark copy, four
     declarations for two distinct shadows. Two of them being verbatim duplicates is
     how the pair drifts: darkening one bar and forgetting the other. */
  --pd-shadow-sticky: 0 -4px 16px rgba(20, 30, 60, 0.08);
  --pd-shadow-sticky-dark: 0 -4px 16px rgba(0, 0, 0, 0.5);

  /* Hover lift of the related-article cards, in the same shape and for the same
     reason as the sticky pair above: an 8 % black shadow is invisible on the
     near-black dark-mode card, so the light and the dark value are two distinct
     shadows rather than one. Written out as a pair here so darkening one and
     forgetting the other is not possible — which is exactly the drift the comment
     on the sticky tokens warns about, and which these two values were still open
     to. */
  --pd-shadow-card-hover: 0 4px 12px rgba(0, 0, 0, 0.08);
  --pd-shadow-card-hover-dark: 0 4px 12px rgba(0, 0, 0, 0.45);

  /* Transition durations. Nine declarations carried four different durations, and two
     of the differences were not decisions: .footer-brand faded in 0.15s beside
     .footer-link at 0.2s in the same footer row, and .demo-image-hover-* animated the
     exact same three properties as .related-article-card — transform, box-shadow,
     border-color — at 0.3s against 0.2s. Gleichartige Bewegungen must feel the same,
     so both were pulled onto the base value. The slow value survives for .badge alone,
     which is a colour cross-fade rather than a movement and reads better unhurried.
     The easing is `ease` throughout without a single exception and is part of the
     token, so a later change cannot move duration and curve apart. Every user of these
     sits inside @media (prefers-reduced-motion: no-preference), which is unchanged. */
  --pd-transition-base: 0.2s ease;
  --pd-transition-slow: 0.3s ease;
}

/* Enable smooth scrolling behavior for better user experience. */
/* Respects prefers-reduced-motion for users with vestibular disorders (WCAG 2.3.3). */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Keep the sticky chrome out of the way when the browser scrolls something into view.
   Reserving space at the end of the document (further down this file) stops the last
   card from being covered, but it does nothing for the browser's own scroll-into-view:
   that stops as soon as the element touches the viewport edge, which is exactly where
   the fixed bar sits. Tabbing through the Arbeitszeugnisanalyse form at 768 px, nine
   stops were measured as 100 % hidden behind the bar at the moment they took focus —
   among them the upload field and both consent switches — which is a failure of WCAG 2.2
   SC 2.4.11 (Focus Not Obscured, AA). scroll-padding tells the browser where the usable
   viewport really ends; it also fixes the #activation anchor, which used to land under
   the navbar. The bottom values match the reserves used further down, so bar height and
   scroll padding cannot drift apart.

   The top value carries a second job that is easy to remove by accident. Every product
   badge on the home page links to /<produkt>/formular/#form, and the cost statement of
   each form page sits in the alert immediately above that anchor. Measured on
   16.09.2026 across four products and six widths from 320 to 1920 px: the jump leaves
   that statement on screen in all 24 cells.

   It does so because of two offsets, and this padding is only one of them — the figures
   are written out because an earlier version of this comment credited it with the whole
   effect and the measurement said otherwise. At 1440 px the price line lands at 100 px
   with both offsets, at 28 px with the theme's scroll-margin alone, at 7 px with this
   padding alone, and at **-65 px, off screen, with neither**. So either one carries it,
   barely, and removing both scrolls past the one sentence that answers what the product
   costs — on the most common entry into the funnel.

   The other offset is Falcon's ``scroll-margin-top: calc(--falcon-top-nav-height +
   1.5rem)`` in the compiled theme, which is a third-party value a theme update can
   change without anyone here noticing. That is the argument for keeping this one: it is
   the half of the margin this project controls. tests/test_css_accessibility_rules.py
   pins it. */
html {
  scroll-padding-top: 4.5rem;
}

html:has(.sticky-cta-bar) {
  scroll-padding-bottom: 4.5rem;
}

html:has(.pd-sticky-cta) {
  scroll-padding-bottom: 7.5rem;
}

/* The scroll padding has to follow the same breakpoint as the content padding
   below, or an anchor jump on the desktop reserves the mobile bar's height and
   leaves a visible gap. It had no desktop counterpart before, so the gap was
   already there at 96 against 72 px; raising the mobile value for the AGB links
   would have widened it to 120 against 72. Measured at 768 px: bar 61 px. */
@media (min-width: 768px) {
  html:has(.pd-sticky-cta) {
    scroll-padding-bottom: 4.5rem;
  }
}

/* The same correction for the form pages' bar, which the rule above had not
   received. `.sticky-cta-bar` is display:none from 992 px up, but :has() is
   structural and does not read display, so the 72 px stayed reserved for a bar
   that is never painted. Measured on the JTA form at 1280 and 1440 px:
   scrollIntoView({block:'nearest'}) on the last form row scrolled to 67 px
   instead of 0. The breakpoint is 992 px, not 768 px, because that is where
   this bar hides - the reserve below uses the same one. */
@media (min-width: 992px) {
  html:has(.sticky-cta-bar) {
    scroll-padding-bottom: 0;
  }
}

@media (max-width: 339.98px) {
  html:has(.sticky-cta-bar) {
    scroll-padding-bottom: 6rem;
  }

  html:has(.pd-sticky-cta) {
    scroll-padding-bottom: 9rem;
  }
}

/* Override Falcon theme footer positioning to prevent content overlap. */
/* The theme uses absolute positioning with a fixed padding-bottom, which breaks with taller footers. */
/* Flexbox column layout ensures the footer always sits below the content regardless of height. */
/* The !important here beats Falcon's own rule and is legitimate. The value behind it
   is a custom property so that the five reserves below can set it by ordinary cascade:
   before, each of them had to beat this declaration with an !important of its own —
   six declarations, five values, one property, and the winner readable only from the
   line number rather than from the cascade. A sixth breakpoint can now be added
   without a seventh !important. */
.content {
  display: flex;
  flex-direction: column;
  padding-bottom: var(--pd-content-pad-bottom, 0) !important;
}

.content > main {
  flex: 1 0 auto;
}

.footer {
  position: static !important;
  flex-shrink: 0;
}

/* The hamburger is a flex item of the top navigation with flex-shrink: 1 and no width
   floor of its own, so as soon as the row runs short it is the element that gives way:
   measured at 20 x 40 px at 320, 375 and 576-620 px, against 40 x 40 px everywhere else.
   Below 24 px it misses WCAG 2.2 SC 2.5.8 (AA), and the spacing exception does not apply
   because the logo link sits 4 px away — on a phone this is the only route into the
   navigation. The row has 17-26 px of slack in exactly those bands, so the 4 px this
   floor costs are affordable; the two size rules at the end of this file already trade
   the same slack for the logo and the theme toggle. */
.navbar-toggler {
  min-width: 24px;
}

/* Footer navigation links rendered as quiet pills that adopt the brand colour on hover and keyboard focus. */
.footer-link {
  display: inline-block;
  padding: 0.2rem 0.55rem;
  border-radius: var(--falcon-border-radius-lg);
  /* Fallback is #5e6e82, the value this file measures at 5.21:1 further down and the
     one the second --falcon-gray-700 fallback already carried. The #4d5969 that stood
     here was 8.4 % off and backed by no measurement in this project: a theme update
     or a stylesheet that fails to load would have rendered this link in a tone nothing
     vouches for. */
  color: var(--falcon-gray-700, #5e6e82);
  text-decoration: none;
  line-height: var(--falcon-body-line-height);
}

/* Focus stays ungated in both directions. A keyboard user exists on touch devices
   too, and prefers-reduced-motion must never remove a focus affordance — only its
   animation. */
.footer-link:focus-visible {
  color: var(--falcon-primary);
  background-color: rgba(var(--falcon-primary-rgb), 0.08);
}

/* Hover behind (hover: hover) so a tap does not leave the link stuck in its hover
   state on iOS and Android, and the transition behind prefers-reduced-motion — the
   two guarantees the file header makes for every rule in this stylesheet. */
@media (hover: hover) {
  .footer-link:hover {
    color: var(--falcon-primary);
    background-color: rgba(var(--falcon-primary-rgb), 0.08);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .footer-link {
    transition:
      color var(--pd-transition-base),
      background-color var(--pd-transition-base);
  }
}

/* Footer brand mark in the baseline row; sits beside the copyright and cookie button. */
/* The vertical padding is load-bearing, not decoration: without it the link box is the
   bare height of its 18 px logo and its line, measured at 110 x 20 px on every page and
   every width, which is under the 24 px floor of WCAG 2.2 SC 2.5.8 (AA). Four pixels top
   and bottom lift it to 28 px; the baseline row is a wrapping flex row with room to
   spare, so nothing else moves. */
.footer-brand {
  color: var(--falcon-gray-800, #344050);
  padding: 0.25rem 0;
}

/* Same two gates as .footer-link above. */
@media (hover: hover) {
  .footer-brand:hover {
    opacity: 0.85;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .footer-brand {
    transition: opacity var(--pd-transition-base);
  }
}

/* Boundary contrast of the form controls (WCAG 2.2 SC 1.4.11, AA, 3:1).
   Measured on /kontakt/ and all four product forms, both themes: the theme's
   --falcon-border-color puts a 1px #d8e2ef edge on white, which is 1.31:1, and
   #344050 on the dark card, which is 1.60:1. The switches sit at 1.82 and 2.36:1.
   With a floating label the border is the ONLY thing that says a field is there, so
   an empty field practically disappears into the card — on the two paid analysis
   products that includes the mandatory upload (1.16:1 against the page).
   --falcon-gray-600 (#748194) is the LIGHTEST token this project already owns that
   clears 3:1 in both themes: measured 3.95:1 on the light field and 4.25 to 4.55:1
   on the dark one, and it happens to be theme-invariant, so one rule covers both.
   No new value enters the project - it is also --falcon-secondary.
   Deliberately scoped to the controls: 1.4.11 asks for the boundary of a USER
   INTERFACE COMPONENT. Card borders, table rules and dividers are decoration and
   keep the lighter tone, so the calm of the pages is untouched. axe does not test
   1.4.11 for a form border, which is why the accessibility run reported zero. */
.form-control,
.form-select,
.form-check-input {
  border-color: var(--falcon-gray-600);
}

/* File upload drop zone component with dashed border styling. */
.filezone {
  border: 2px dashed var(--falcon-border-color);
  border-radius: var(--falcon-border-radius-lg);
  text-align: center;
  background-color: var(--falcon-body-bg);
}

/* Active state styling when files are dragged over the drop zone. Uses the
   theme-aware primary subtle background so the highlight visibly differs from the
   resting --falcon-body-bg in both light and dark mode. The former
   --falcon-input-bg variable was undefined, so the rule was invalid at
   computed-value time and the zone fell back to transparent (no drop feedback). */
/* :focus-within joins .dragover here because the zone had feedback for the pointer
   and none for the keyboard: tabbing to the file input inside it changed nothing on
   the zone itself, so the only upload control of the two analysis products gave a
   keyboard user no sign of where the focus had landed. Same declaration, same
   visual language - no new state is being invented, the existing one is extended to
   the second input method. The input's own focus ring is unaffected. */
.filezone.dragover,
.filezone:focus-within {
  background-color: var(--falcon-primary-bg-subtle);
}

/* Custom styling for the native file selector button to match brand colors. */
input[type="file"]::file-selector-button {
  background-color: var(--falcon-primary) !important;
  color: var(--light-bg) !important;
}

/* Resting look of a submit button whose consent switches are not both ticked.
   0.65 is stock Bootstrap's disabled opacity, but THIS theme fades a genuinely
   disabled button to 0.5 (--falcon-btn-disabled-opacity), so the two do not look
   alike and the value is not being aligned: the button here is still operable, and
   fading an operable control further than a dead one costs legibility for no gain.
   Wahrnehmbarkeit before Systemtreue. It deliberately omits
   pointer-events: none, because the button is no longer disabled: it stays in the
   tab order and in the accessibility tree so it can announce itself and explain the
   refusal (see initializeCheckboxToggle in custom.js). Suppressing pointer events
   here would silently restore the old behaviour for mouse users, who would click a
   dead control and receive nothing. */
.pd-consent-pending {
  opacity: 0.65;
  cursor: not-allowed;
}

/* Adjust padding for cookie consent banner main container. */
.termsfeed-com---nb .cc-nb-main-container {
  padding: 2rem !important;
}

/* Override default cookie consent banner dimensions and styling for simplified layout. */
.termsfeed-com---nb-simple,
.termsfeed-com---nb-simple .termsfeed-com---nb .cc-nb-main-container {
  max-width: 100% !important;
  width: 100% !important;
  padding: 0 !important;
  font-weight: 300 !important;
  border-top: 1px solid var(--light-grey) !important;
}

/* Base styling for light theme cookie consent banner and buttons. */
.termsfeed-com---palette-light.termsfeed-com---nb,
.termsfeed-com---palette-light .cc-nb-okagree,
.termsfeed-com---palette-light .cc-nb-reject {
  background-color: var(--light-bg) !important;
  font-size: 0.875rem !important;
}

/* Primary button styling for cookie consent accept button. */
.termsfeed-com---palette-light .cc-nb-okagree {
  background-color: var(--falcon-primary) !important;
}

/* Secondary button styling for cookie consent reject button. */
.termsfeed-com---palette-light .cc-nb-reject {
  background-color: var(--light-grey) !important;
  color: var(--dark-text) !important;
}

/* Pin the TermsFeed notice banner to the bottom of the viewport. The headline
   banner type renders at the top by default; this overrides it so the consent
   prompt sits at the foot of the page. The z-index stays above the sticky CTA
   bar and navbar (1030) but below Bootstrap modals (1055) and tooltips (1080)
   so a dismissible consent prompt never hides a modal dialog. */
.termsfeed-com---nb {
  position: fixed !important;
  top: auto !important;
  bottom: 0 !important;
  left: 0 !important;
  right: 0 !important;
  z-index: 1045 !important;
}

/* Highlight active navigation links with primary brand color. */
.navbar-top .navbar-nav .nav-link.active {
  color: var(--falcon-primary) !important;
}

/* Apply hover and focus states to navigation links for better interactivity. */
.navbar-top .navbar-nav .nav-link.text-body:hover,
.navbar-top .navbar-nav .nav-link.text-body:focus {
  color: var(--falcon-nav-link-hover-color) !important;
}

/* Apply pulse animations to CTA buttons for visual emphasis and user attention. */
/* Both variants are gated behind prefers-reduced-motion (WCAG 2.3.3); the keyframes */
/* themselves stay outside the media query so the rule remains valid even if an external */
/* stylesheet later opts a single element into the animation. */
@media (prefers-reduced-motion: no-preference) {
  .pulse {
    animation: pulse-animation 2s infinite;
  }

  .pulse-info {
    animation: pulse-info-animation 2s infinite;
  }
}

/* Pulse keyframes for the primary CTA: scales up and emits a fading ring using the Falcon primary RGB token. */
@keyframes pulse-animation {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(var(--falcon-primary-rgb), 0.7);
  }

  70% {
    transform: scale(1.05);
    box-shadow: 0 0 0 10px rgba(var(--falcon-primary-rgb), 0);
  }

  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(var(--falcon-primary-rgb), 0);
  }
}

/* Pulse keyframes for the info CTA: scales up and emits a fading ring using the Falcon info RGB token. */
@keyframes pulse-info-animation {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(var(--falcon-info-rgb), 0.7);
  }

  70% {
    transform: scale(1.05);
    box-shadow: 0 0 0 10px rgba(var(--falcon-info-rgb), 0);
  }

  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(var(--falcon-info-rgb), 0);
  }
}

/* Demo image hover effect for primary-colored product GIFs. */
.demo-image-hover-primary {
  cursor: pointer;
}

/* Demo image hover effect for warning-colored product GIFs. */
.demo-image-hover-warning {
  cursor: pointer;
}

/* Apply hover transitions and transforms only when:
 *   1. the user has no motion preference (WCAG 2.3.3), AND
 *   2. the primary pointer is a real hover-capable device (mouse/trackpad).
 * On touch screens the :hover state stays "stuck" after a tap until the user
 * taps elsewhere, leaving the demo image visibly scaled — wrapping in
 * @media (hover: hover) suppresses that on phones and tablets. */
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .demo-image-hover-primary {
    transition:
      transform var(--pd-transition-base),
      box-shadow var(--pd-transition-base),
      border-color var(--pd-transition-base);
  }

  .demo-image-hover-primary:hover {
    transform: scale(1.02) translateY(-2px);
    box-shadow: 0 12px 30px rgba(var(--falcon-primary-rgb), 0.2);
    border-color: var(--falcon-primary) !important;
  }

  .demo-image-hover-warning {
    transition:
      transform var(--pd-transition-base),
      box-shadow var(--pd-transition-base),
      border-color var(--pd-transition-base);
  }

  .demo-image-hover-warning:hover {
    transform: scale(1.02) translateY(-2px);
    box-shadow: 0 12px 30px rgba(var(--falcon-warning-rgb), 0.2);
    border-color: var(--falcon-warning) !important;
  }
}

/* Add smooth transition effect for badge background color changes. */
/* Respects prefers-reduced-motion for users with vestibular disorders (WCAG 2.3.3). */
@media (prefers-reduced-motion: no-preference) {
  .badge {
    transition: background-color var(--pd-transition-slow);
  }
}

/* Darken primary badge color on parent anchor hover for visual feedback. */
a:hover .badge.bg-primary {
  background-color: var(--badge-primary-hover) !important;
}

/* Darken info badge color on parent anchor hover for visual feedback. */
a:hover .badge.bg-info {
  background-color: var(--badge-info-hover) !important;
}

/* Darken success badge color on parent anchor hover for visual feedback. */
a:hover .badge.bg-success {
  background-color: var(--badge-success-hover) !important;
}

/* Darken warning badge color on parent anchor hover for visual feedback. */
a:hover .badge.bg-warning {
  background-color: var(--badge-warning-hover) !important;
}

/* Apply primary color to FAQ h3 headings within card bodies for consistent styling. */
/* Excludes Kündigungsanalyse (text-warning) and Arbeitszeugniserstellung (text-info) per product color scheme. */
.card-body h3.h6:not(.text-warning):not(.text-info) {
  color: var(--falcon-primary);
}

/* Hide Django-Daisy SVG icons in checkbox labels to maintain classic checkbox appearance. */
.toggle svg {
  display: none !important;
}

/* Ensure toggle labels do not create unwanted spacing or layout shifts. */
/* Using display:contents makes the toggle wrapper invisible to layout while keeping its children. */
.toggle {
  padding: 0 !important;
  margin: 0 !important;
  display: contents !important;
}

/* Reset toggle label styling to prevent DaisyUI interference with checkbox layout. */
.toggle::before,
.toggle::after {
  content: none !important;
  display: none !important;
}

/* Maintain proper alignment for checkboxes without toggle styling effects. */
.form-check .toggle {
  background: none !important;
  border: none !important;
  width: auto !important;
  height: auto !important;
}

/* Force checkbox and label side-by-side alignment on all screen sizes. */
.form-switch .form-check {
  display: flex !important;
  align-items: flex-start !important;
  padding-left: 0 !important;
}

/* Position the checkbox input correctly within the flex container. */
.form-switch .form-check .form-check-input {
  float: none !important;
  margin-left: 0 !important;
  margin-top: 0.1875rem;
  flex-shrink: 0;
}

/* Ensure the label text flows naturally beside the checkbox. */
.form-switch .form-check .form-check-label {
  margin-left: 0.5rem;
}

/* Shared base styles for product demo videos in CTA sections, to avoid inline style repetition. */
/* The explicit aspect-ratio reserves the box before anything is loaded. With             */
/* preload="none" the width/height attributes alone did not: Chrome treated the <video>   */
/* as unsized until its poster arrived, and the box then grew and pushed the cards below  */
/* it down (CLS 0.25 on a throttled mobile trace of the pillar article, 23.09.2026). All  */
/* three posters and videos are 960 x 600, hence 16 / 10.                                 */
.demo-video {
  border-width: 2px !important;
  width: 100%;
  height: auto;
  max-width: 100%;
  aspect-ratio: 16 / 10;
}

/* Primary-colored gradient background for analysis product demo videos. */
/* The light endpoint uses the Falcon emphasis-bg token (the card surface, white in light */
/* mode, dark navy in dark mode) so the wash adapts to the active theme instead of staying */
/* white. In light mode --falcon-emphasis-bg-rgb is 255,255,255, preserving the original look. */
.demo-video-primary {
  background: linear-gradient(
    135deg,
    rgba(var(--falcon-primary-rgb), 0.05) 0%,
    rgba(var(--falcon-emphasis-bg-rgb), 0.95) 100%
  );
}

/* Warning-colored gradient background for termination product demo videos. */
/* Same theme-aware light endpoint as .demo-video-primary (see note above). */
.demo-video-warning {
  background: linear-gradient(
    135deg,
    rgba(var(--falcon-warning-rgb), 0.05) 0%,
    rgba(var(--falcon-emphasis-bg-rgb), 0.95) 100%
  );
}

/* Sticky CTA bar on mobile for formular pages. */
/* Keeps the submit button visible at the bottom of the viewport on small screens. */
@media (max-width: 991.98px) {
  .sticky-cta-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1030;
    background: var(--falcon-body-bg);
    border-top: 1px solid var(--falcon-border-color);
    padding: 0.75rem 1rem;
    box-shadow: var(--pd-shadow-sticky);
  }

  /* Stronger upward shadow in dark mode to keep the bar visually separated from the dark page. */
  [data-bs-theme="dark"] .sticky-cta-bar {
    box-shadow: var(--pd-shadow-sticky-dark);
  }

  body:has(.sticky-cta-bar) .content {
    --pd-content-pad-bottom: 4.5rem;
  }
}

/* Hide the mobile sticky submit bar on desktop viewports where the form button is already visible. */
@media (min-width: 992px) {
  .sticky-cta-bar {
    display: none;
  }
}

/* Related articles section styling for consistent visual integration with blog content. */
.related-articles {
  border-top: 1px solid var(--falcon-border-color);
}

/* Related articles header styling matching article heading conventions. */
.related-articles-header {
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--falcon-border-color);
}

/* Related article card hover and transition effects for interactive feedback. */
/* Respects prefers-reduced-motion (WCAG 2.3.3) and is gated behind hover: hover so the */
/* effect does not stick on touch devices after a tap. */
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .related-article-card {
    transition:
      transform var(--pd-transition-base),
      box-shadow var(--pd-transition-base),
      border-color var(--pd-transition-base);
  }

  .related-article-card:hover,
  .related-article-card:focus-within {
    transform: translateY(-2px);
    box-shadow: var(--pd-shadow-card-hover);
    border-color: var(--falcon-primary) !important;
  }

  /* An 8 % black shadow is invisible on the near-black dark-mode card background, */
  /* so the hover lift loses its depth cue. Matches the sticky-bar override above. */
  [data-bs-theme="dark"] .related-article-card:hover,
  [data-bs-theme="dark"] .related-article-card:focus-within {
    box-shadow: var(--pd-shadow-card-hover-dark);
  }

  .related-article-card.related-article-card--warning:hover,
  .related-article-card.related-article-card--warning:focus-within {
    border-color: var(--falcon-warning) !important;
  }

  .related-article-arrow {
    transition:
      transform var(--pd-transition-base),
      color var(--pd-transition-base);
  }

  /* The hover tone is the theme-aware `*-text-emphasis`, not the raw brand token,
     for the same reason the link and button rules further down carry it. Measured
     on 16.09.2026 with the pointer on the card, against the effective background
     walked up the ancestor chain: the raw token gave 4.06:1 on the dark card and
     4.14:1 on the light one, both short of the 4.5:1 of WCAG 2.2 AA (1.4.3); the
     emphasis tone gives 10.00:1 and 6.58:1. The same correction was made for the
     ratgeber arrows below, measured at 3.88:1 -> 9.56:1 dark and 3.97:1 -> 6.30:1
     light. One rule covers both themes because the variable is theme-dependent —
     a hex literal would fix one theme and move the other the wrong way. */
  .related-article-card:hover .related-article-arrow {
    transform: translateX(3px);
    color: var(--falcon-primary-text-emphasis) !important;
  }

  .related-article-card.related-article-card--warning:hover .related-article-arrow {
    color: var(--falcon-warning-text-emphasis) !important;
  }
}

/* Ratgeber link rows on the homepage Top-Ratgeber card and the four product formular */
/* pages: icon-led article links that replace the former bullet list and reuse the */
/* related-articles hover language (sliding arrow, subtle row tint) so the section sits */
/* naturally in the Falcon design. */
.pd-ratgeber-link {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0.55rem;
  border-radius: var(--falcon-border-radius-lg);
  color: var(--falcon-gray-900);
}

/* Pin every icon to one fixed-width column so the link text starts at the same */
/* indent across all three columns. The fixed width is load-bearing: measured, the five */
/* icons in this list have five different intrinsic advances (12, 14, 16, 17 and 18 px), */
/* so without it the labels start at five different x positions. */
/* No !important: the earlier comment here claimed FontAwesome renders each glyph as an */
/* <svg class="svg-inline--fa"> whose own width rule had to be beaten. That was true of */
/* the SVG/JS delivery mode, which this project left on 19.08.2026 - it now loads the CSS */
/* plus webfont build, where svg.svg-inline--fa occurs 0 times in the DOM (measured) and */
/* nothing competes for width on this span. */
.pd-ratgeber-link__icon {
  width: 1.25rem;
  text-align: center;
  flex-shrink: 0;
}

/* The label between icon and arrow is a flex item, and a flex item's automatic minimum
   size keeps it from shrinking below its longest word. German compounds like
   "Kündigungsschutzklage" and "Standardformulierungen" therefore hold the row open, and
   the whole page scrolls sideways as soon as the type grows: measured on the homepage at
   375 px, the document overflowed by 10 px at a browser base font of 18 px and by 81 px
   at 24 px. min-width: 0 releases that floor and hyphens: auto lets the browser break the
   word properly instead of mid-syllable — the document is lang="de", so Chromium and
   Firefox apply real German hyphenation patterns. This is the same pair the
   .pd-overview-row rule at the end of this file already uses for the same reason. */
.pd-ratgeber-link > .flex-1 {
  min-width: 0;
  hyphens: auto;
}

.pd-ratgeber-arrow {
  margin-left: auto;
  flex-shrink: 0;
}

/* Hover affordances are gated behind hover-capable pointers and motion preference */
/* so the tint and arrow slide never stick after a tap on touch devices (WCAG 2.3.3). */
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .pd-ratgeber-link {
    transition: background-color var(--pd-transition-base);
  }

  .pd-ratgeber-link:hover,
  .pd-ratgeber-link:focus-visible {
    background-color: var(--falcon-tertiary-bg);
  }

  .pd-ratgeber-arrow {
    transition:
      transform var(--pd-transition-base),
      color var(--pd-transition-base);
  }

  .pd-ratgeber-link:hover .pd-ratgeber-arrow {
    transform: translateX(3px);
    color: var(--falcon-primary-text-emphasis) !important;
  }

  .pd-ratgeber-link--warning:hover .pd-ratgeber-arrow {
    color: var(--falcon-warning-text-emphasis) !important;
  }

  .pd-ratgeber-link--info:hover .pd-ratgeber-arrow {
    color: var(--falcon-info-text-emphasis) !important;
  }
}

/* Reporting improvement block: green-tinted highlight for AI-suggested rewrites. */
.pd-improvement-block {
  margin-top: 0.75rem;
  padding: 0.75rem 0.875rem;
  background: rgba(var(--falcon-success-rgb), 0.08);
  border-left: 3px solid var(--falcon-success);
  border-radius: var(--falcon-border-radius-lg);
}

/* Slightly stronger green tint in dark mode so the success accent stays readable on a dark surface. */
[data-bs-theme="dark"] .pd-improvement-block {
  background: rgba(var(--falcon-success-rgb), 0.14);
}

/* Reporting hint block: subdued italic explanation under each improvement. */
.pd-hint-block {
  margin-top: 0.5rem;
  padding-left: 0.875rem;
  border-left: 2px dashed var(--falcon-gray-400);
  color: var(--falcon-gray-700);
  font-style: italic;
  font-size: 0.8333333333rem;
}

/* Footnote markers keep a floor, because their relative shrink compounds.
   The six <sup> in the project are the footnote pair of the three activation CTAs,
   and the browser's own 0.75em applies to whatever the parent already is: measured
   on the report page, the marker beside the comparison price renders at 10px and the
   one on the citation line at 8.33px, because that line is already fs-11 (11.11px).
   8.33px is not readable, and the marker is the only thing tying the 226,10 € claim
   to the § 34 RVG source that backs it — an unreadable pointer leaves a price
   comparison looking unsupported on the conversion path. `max()` keeps the relative
   behaviour wherever it still works and only puts a floor under it; support is
   Chrome 79+, Safari 11.1+, Firefox 75+, and a browser without it falls back to the
   inherited 0.75em, which is today's rendering. */
sup,
sub {
  font-size: max(0.75em, 11px);
}

/* Blog article body spacing. */
/* Templates use `mb-0` on every paragraph to keep <li><p> tight inside lists, */
/* which collapses standalone body paragraphs into a wall of text. These rules */
/* restore vertical rhythm on direct children of the article `.card-body` only — */
/* list items, includes, and non-article cards are unaffected. */
body.is-blog-article .card-body > p.fs-10 {
  margin-bottom: 1rem !important;
}

body.is-blog-article .card-body > h2 {
  margin-top: 2rem;
}

body.is-blog-article .card-body > h2:first-of-type {
  margin-top: 0;
}

body.is-blog-article .card-body > h3 {
  margin-top: 1.25rem;
  margin-bottom: 0.5rem;
}

body.is-blog-article .card-body > ul.fs-10,
body.is-blog-article .card-body > ol.fs-10 {
  margin-bottom: 1.25rem;
}

body.is-blog-article .card-body > ul.fs-10 li + li,
body.is-blog-article .card-body > ol.fs-10 li + li {
  margin-top: 0.4rem;
}

body.is-blog-article .card-body > .table-responsive {
  margin-top: 0.5rem;
  margin-bottom: 1.25rem;
}

body.is-blog-article .card-body > hr {
  margin-top: 1.25rem;
  margin-bottom: 1.25rem;
  opacity: 0.15;
}

/* Column widths for the three recurring article table shapes. They used to be 19 */
/* inline `style="width: N%"` attributes across three articles, in five different */
/* distributions for what are really three layouts: a grade table went 8/15/77 in one */
/* article, 10/20/70 in the next and 10/15/75 in the third. Nothing rendered wrong, */
/* but the proportions were re-invented per article and a change on narrow screens */
/* meant editing every attribute by hand. Declaring them once also settles which */
/* distribution is the right one. */
/* Note (grade) | rating | example formulation. The example column carries the long */
/* prose and gets the room; the grade column holds a single digit. */
.pd-table-grades th:nth-child(1) {
  width: 10%;
}

.pd-table-grades th:nth-child(2) {
  width: 20%;
}

.pd-table-grades th:nth-child(3) {
  width: 70%;
}

/* Note (grade) | satisfaction formula. */
.pd-table-formula th:nth-child(1) {
  width: 10%;
}

.pd-table-formula th:nth-child(2) {
  width: 90%;
}

/* Criterion | three comparable roles, each of which needs the same room. */
.pd-table-compare th:nth-child(1) {
  width: 19%;
}

.pd-table-compare th:nth-child(n + 2) {
  width: 27%;
}

/* Reading measure for the long form. Nothing in the parent chain limited the text
   column: `.container { max-width: 1480px }` is the only bound, so a paragraph ran
   the full width of the card. Measured on the pillar article, characters per
   rendered line (text length over line-box count): 103 median at 1280 px and 127,5
   at 1920 px, against the 45-75 band that carries body copy. At 375 px it was
   already 38,5, which is why this rule is a ceiling and never a width — mobile must
   not be touched.

   The limit is deliberately NOT centred and deliberately NOT on `.card-body`.
   Centring would break the left alignment the text shares with every heading,
   table and embedded CTA in the same card, and putting it on the container would
   squeeze the tables and the product CTAs, which need the width. Only the elements
   that carry running text are bounded; a table beside them still spans the card.

   `ch` rather than `px` so the measure follows the font size: it is the width of
   the digit zero, and in a proportional face about 68 of them carry roughly 72
   average characters. Should the body size ever move (K19-i1-02), the measure moves
   with it instead of silently becoming wrong. */
body.is-blog-article .card-body > p.fs-10,
body.is-blog-article .card-body > ul.fs-10,
body.is-blog-article .card-body > ol.fs-10,
body.is-blog-article .card-body > h2,
body.is-blog-article .card-body > h3 {
  max-width: 68ch;
}

/* Tabular figures in tables and in the large display size. Measured on the pillar
   article: all 18 numeric table cells rendered with proportional figures, so the price
   and grade columns of the comparison tables did not line up vertically — a 1 is
   narrower than a 4, and the decimal points drift across the rows. The Kündigungsanalyse
   report's severance amount uses the same setting so its 47.8 px figure keeps even
   spacing. This is a font feature, not a size or colour change: nothing reflows, because
   tabular figures are the same advance width the layout already reserves for the widest
   digit. */
td,
th,
.fs-3 {
  font-variant-numeric: tabular-nums;
}

/* Mobile horizontal-scroll affordance for wide (3+ column) article tables. */
/* initializeScrollableTables() in custom.js tags any overflowing .table-responsive with */
/* .pd-scrollable and inserts a .pd-table-hint label before it; without this cue the */
/* rightmost column is silently clipped on phones. The right-edge mask fades the content so */
/* the clipping reads as "more to the right", and lifts once the user scrolls to the end. */
/* --falcon-gray-700, not -600: at 12px the 600 tone measures 3.95:1 against the card, */
/* below the 4.5:1 of WCAG 2.2 AA (1.4.3), and this label is the only cue that the */
/* rightmost table column is clipped. It is the same substitution .form-text already */
/* makes for the same reason. */
.pd-table-hint {
  display: none;
  margin-bottom: 0.35rem;
  font-size: 0.75rem;
  color: var(--falcon-gray-700, #5e6e82);
  text-align: right;
}

@media (max-width: 575.98px) {
  .pd-table-hint:has(+ .table-responsive.pd-scrollable) {
    display: block;
  }

  .table-responsive.pd-scrollable:not(.pd-scroll-end) {
    -webkit-mask-image: linear-gradient(to right, #000 86%, transparent 100%);
    mask-image: linear-gradient(to right, #000 86%, transparent 100%);
  }
}

/* Sticky bottom CTA bar shown on paid product reporting pages until activation. */
/* Positioning, theme-aware background (var(--falcon-tertiary-bg)) and top border come from */
/* the Bootstrap/Falcon utilities .fixed-bottom .bg-body-tertiary .border-top on the element. */
/* Only the upward shadow and iOS safe-area inset stay here — neither has a utility class. */
.pd-sticky-cta {
  box-shadow: var(--pd-shadow-sticky);
  padding-bottom: env(safe-area-inset-bottom);
}

/* Stronger upward shadow in dark mode to keep the sticky bar visually separated from the dark page. */
[data-bs-theme="dark"] .pd-sticky-cta {
  box-shadow: var(--pd-shadow-sticky-dark);
}

/* Reserve room at the bottom of the main content so the sticky bar never covers the last card. */
/* 7.5rem matches the mobile bar, whose guarantee line now also carries the AGB and privacy */
/* links: the bar measured 91 px before those links and 115 px with them, because the line */
/* wraps below roughly 420 px. The desktop override below stays at 4.5rem — there the links */
/* ride along on the existing "Einmalzahlung" line, which takes the bar from 57 px to 61 px, */
/* still well inside the 72 px reserved. Both figures measured, not estimated. */
body:has(.pd-sticky-cta) .content {
  --pd-content-pad-bottom: 7.5rem;
}

@media (min-width: 768px) {
  body:has(.pd-sticky-cta) .content {
    --pd-content-pad-bottom: 4.5rem;
  }
}

/* At exactly 320 px both sticky bars gain one more line and outgrow the space reserved
   for them, so the last content card stays covered no matter how far the visitor scrolls.
   Measured across 320-768 px: .pd-sticky-cta is 115 px from 340 px upwards but 139 px at
   320 px, .sticky-cta-bar is 61 px from 340 px upwards but 85 px at 320 px (reserve
   72 px). In both cases the price line stops fitting beside the guarantee. Raising each
   reserve by one line height in that band is the smaller change than shortening the copy
   — the price line carries the tax notice that § 312j Abs. 2 BGB and § 3 PAngV require,
   and the guarantee line now carries the AGB and privacy links, so that the bar, which is
   a full second entry into the purchase, offers them where it is used rather than only in
   the page footer. The 340 px boundary is the one the theme-toggle rule at the end of this
   file already uses. */
@media (max-width: 339.98px) {
  body:has(.pd-sticky-cta) .content {
    --pd-content-pad-bottom: 9rem;
  }

  body:has(.sticky-cta-bar) .content {
    --pd-content-pad-bottom: 6rem;
  }
}

/* Links inside running text were marked by colour alone. Measured: the link blue
   (#2c7be5) against the body ink (#344050) is 2.54:1, where WCAG 2.2 SC 1.4.1 (A) wants
   either 3:1 plus a non-colour cue on hover and focus, or a permanent non-colour cue.
   axe counted 148 such links in light mode across 21 surfaces — the three pillar articles
   alone hold 112 — and they include the consent links to the AGB and the privacy policy
   on the forms, which a reader has to be able to find.
   The rule is deliberately narrow: only anchors inside a paragraph or list item of a card
   body, never buttons, badges, cards or navigation, all of which carry their own shape.
   Verified against the rendered pages of the blog, the legal texts, a report and the
   homepage — 0 of 99 matched anchors were a styled control. The offset keeps the rule
   clear of descenders in Open Sans. */
.card-body p a:not(.btn):not([class*="badge"]),
.card-body li a:not(.btn):not([class*="badge"]),
.card-body h2 a:not(.btn),
.card-body h3 a:not(.btn),
.form-check-label a:not(.btn) {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Focus indicator reinforcement (WCAG 2.4.7). */
/* Falcon and Bootstrap occasionally remove the native outline on buttons/links. */
/* :focus-visible only triggers for keyboard users — mouse clicks remain visually clean. */
/* One ring, drawn as an outline with a 2px offset. An earlier version of this comment */
/* promised a double ring of box-shadow plus outline; there is no box-shadow here, and */
/* the rule below this one deliberately clears any that Falcon leaves behind. The offset */
/* is what separates the ring from the control, so a second ring buys nothing. */
:focus-visible {
  outline: 2px solid var(--falcon-primary);
  outline-offset: 2px;
}

/* The bare :focus-visible rule above (specificity 0,1,0) loses to Falcon's own */
/* .nav-link:focus-visible{outline:0} (0,2,0) and .navbar .nav-link:focus-visible */
/* {box-shadow:none} (0,3,0) in theme.min.css, so every link in the vertical */
/* navigation and the top navbar was measurably focus-invisible — 11 consecutive */
/* tab stops on every page, directly after the skip link. Matching the higher */
/* specificity is the whole point of this rule; do not "simplify" it back into the */
/* selector above. Verify with getComputedStyle after a real Tab keypress, not after */
/* element.focus() — :focus-visible does not necessarily match on programmatic focus. */
/* .btn is in the same list for the same reason: Falcon's .btn:focus-visible{outline:0} */
/* (0,2,0) also beats the bare rule. Buttons were not invisible — the outline-* */
/* variants invert their colours on focus — but that state is identical to hover, so */
/* a keyboard user could not tell focus from a hovered button, and it departed from */
/* the 2px outline used everywhere else. */
.navbar .nav-link:focus-visible,
.navbar-vertical .nav-link:focus-visible,
.nav-link:focus-visible,
.btn:focus-visible {
  outline: 2px solid var(--falcon-primary);
  outline-offset: 2px;
  box-shadow: none;
}

/* The form controls were missing from the list above, for the same reason and with a
   worse outcome: .form-control:focus and .form-select:focus set outline: 0 (0,2,0) and
   beat the bare :focus-visible rule, and the box-shadow Bootstrap normally puts in its
   place resolves here to a fully transparent ring — measured on every field of every
   form, in all three engines, as `outline: 0px none` plus
   `rgba(0, 0, 0, 0) 0px 0px 0px 0px`, with the resting border colour unchanged. The
   result was a form whose focus was invisible from the first field on: the upload
   control of the two analysis products, every text field of the Arbeitszeugniserstellung
   and both consent switches (WCAG 2.2 SC 2.4.7, AA). Verified with real Tab presses, not
   element.focus() — :focus-visible does not necessarily match on programmatic focus. */
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible {
  outline: 2px solid var(--falcon-primary);
  outline-offset: 2px;
}

/* Contrast repair for two Falcon defaults that fail WCAG 1.4.3 (AA) at 4.5:1. */
/* Both use --falcon-gray-500 (#9da9bb), which measures 2.38:1 on white, 2.12:1 on the */
/* page background and 2.28:1 on the legal pages' card. Neither is decoration: */
/* .form-text carries the upload limits and the salary bounds — exactly the rules whose */
/* violation produces the upload error — and the legal sidebar entries are links. */
/* --falcon-gray-700 (#5e6e82) measures 5.21:1 on white, 4.99:1 on the legal pages' */
/* card and 4.63:1 on the page background — above AA in every context where these two */
/* selectors actually render. This is a local override, not a change to the palette. */
.form-text,
.terms-sidebar .nav-link {
  color: var(--falcon-gray-700);
}

/* The floating labels were the third place the same grey slips below AA, and the one
   that matters most: the label IS the field's name, and while the field is empty it is
   the only thing that says what belongs in it. Falcon fades --falcon-gray-700 to 65 %
   opacity, which composites to #96a1ae on the white card — measured 2.63:1 in light mode
   and 3.77-3.90:1 on the two dark surfaces, against the 4.5:1 of WCAG 2.2 SC 1.4.3 (AA).
   Full opacity is the smallest step that clears it in light mode (0.94 would be enough,
   but a value that close to 1 buys nothing) and lifts the dark surfaces to 7.06-7.57:1.
   The grey itself is unchanged; only the veil over it is removed. */
.form-floating > label {
  opacity: 1;
  color: var(--falcon-gray-700);
}

/* Skip link must rise above the sticky CTA (z-index 1030) when focused so keyboard users */
/* can always escape to the main content. Falcon's .visually-hidden-focusable handles */
/* hide/reveal; we only need to bump stacking. :focus-within covers the case where the */
/* anchor itself is not the focus target but a descendant is (defensive double-binding). */
/* The 1090 value intentionally sits just above Bootstrap's tooltip layer (1080) — */
/* a tooltip must never obscure the only keyboard escape route to the main content. */
.visually-hidden-focusable:focus,
.visually-hidden-focusable:focus-within {
  z-index: 1090;
}

/* Forced colors / Windows High Contrast Mode (WCAG 1.4.11). */
/* Strip decorative gradients and shadows so system colours can repaint affordances cleanly. */
/* Pulse animations are suppressed because the animated box-shadow conveys nothing without */
/* color (forced-colors removes shadows anyway, leaving only a meaningless scale tick). */
@media (forced-colors: active) {
  .pulse,
  .pulse-info {
    animation: none !important;
  }

  /* .pd-improvement-block used to be listed here too. It sets no box-shadow
     anywhere in this file, so the declaration neutralised nothing.
     Counted, this file sets a real box-shadow in ten places: the two
     .demo-image-hover-* hovers, .sticky-cta-bar and its dark twin,
     .related-article-card:hover/:focus-within and its dark twin, .pd-sticky-cta
     and its dark twin, and the two .form-range:focus-visible thumb rules.
     Only .pd-sticky-cta is cleared here, and the reason is that its shadow is the
     one that would otherwise be redrawn as a solid system colour and read as a
     second border above the .border-top the include already carries. The seven
     decorative ones need no rule - forced-colors drops them by itself. The two
     .form-range thumb rules must NOT be added: they are the focus indicator of the
     three rating sliders, the one place where outline cannot be used (see their own
     comment), and clearing them would take the focus ring off the controls that
     decide the grades of a purchased certificate. */
  .pd-sticky-cta {
    box-shadow: none !important;
  }

  .demo-video-primary,
  .demo-video-warning {
    background: Canvas !important;
  }

  :focus-visible {
    outline: 2px solid CanvasText;
  }
}

/* Print stylesheet for reports and blog articles. */
/* Users frequently print/save-as-PDF analysis reports for HR meetings or labour court */
/* filings — hide all interactive chrome (navbar, sticky CTA, footer, related articles) */
/* so the printed page contains only the substantive content. */
@media print {
  .navbar-top,
  .navbar-vertical,
  .footer,
  .pd-sticky-cta,
  .sticky-cta-bar,
  .related-articles,
  .pulse,
  .pulse-info,
  .visually-hidden-focusable,
  .termsfeed-com---nb {
    display: none !important;
  }

  body,
  .content,
  .content > main,
  .card,
  .card-body {
    background: #ffffff !important;
    box-shadow: none !important;
    color: #000000 !important;
  }

  a {
    color: #000000 !important;
    text-decoration: underline;
  }

  /* Avoid orphan headings on page breaks. */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    page-break-after: avoid;
    break-after: avoid;
  }

  /* Keep tables and lists together where possible. */
  table,
  ul,
  ol {
    page-break-inside: avoid;
    break-inside: avoid;
  }

  /* Hide gated/blurred content so the paywall is preserved when printing.
       Paid users never receive the blur markup (template-conditional), so this
       rule only affects unpaid prints. Both the class-based JRA/JCA blur
       (.pd-blur-lock / .pd-blur-lock-block) and the inline-styled JRC overlay
       (backdrop-filter) must be matched here: print engines frequently drop
       CSS filter/backdrop-filter, which would otherwise leak the paid grades. */
  [style*="blur"],
  [style*="backdrop-filter"],
  .pd-blur-lock,
  .pd-blur-lock-block {
    display: none !important;
  }

  /* Print improvement/hint side-bar blocks as plain readable paragraphs
       without the colored borders/backgrounds that confuse on paper. */
  .pd-improvement-block,
  .pd-hint-block {
    background: transparent !important;
    border-left: none !important;
    color: #000000 !important;
    padding-left: 0 !important;
  }
}

/* Compact top-nav product CTAs (Analysen / Erstellungen). Slimmer than a default
   button so the bar stays light: reduced vertical padding and a 34 px min-height
   that still satisfies the WCAG 2.5.8 (AA) 24 px target. On phones the labels are
   hidden (icon only), so the horizontal padding is tightened further. */
.pd-nav-cta {
  min-height: 34px;
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
}

@media (max-width: 575.98px) {
  .pd-nav-cta {
    min-height: 32px;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

/* Theme toggle in the top navbar sized to the 44 px AAA touch target (WCAG 2.5.5) on tablet and up. */
.pd-theme-toggle {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* The Falcon theme hides the toggle's checkbox with display: none and paints the */
/* two icon labels instead. display: none also removes the element from the tab */
/* order and from the accessibility tree, which left the light/dark switch usable */
/* by pointer only — it did not exist for keyboard or screen reader users at all */
/* (WCAG 2.1.1 Level A, 4.1.2 Level A). Hiding it visually instead keeps it */
/* focusable and operable with Space, while the icon labels keep rendering the */
/* visual state. The 1 px box plus pointer-events: none prevents it from */
/* intercepting clicks meant for the labels. */
.pd-theme-toggle .theme-control-toggle-input {
  display: block;
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* Keyboard focus lands on the invisible checkbox, so the ring has to be drawn on */
/* the visible labels next to it. */
.pd-theme-toggle .theme-control-toggle-input:focus-visible ~ .theme-control-toggle-label {
  outline: 2px solid var(--falcon-primary);
  outline-offset: 2px;
  border-radius: var(--falcon-border-radius-sm);
}

/* The Falcon table tokens are defined once without a dark-mode counterpart, so */
/* table cells kept the light-mode ink (#5e6e82) on the dark card background — */
/* measured at 3.22:1, below the 4.5:1 AA floor, while the surrounding body copy */
/* correctly remaps to #9da9bb. This affects every comparison table in the blog */
/* and the sentence table of the paid report. Both tokens are needed: striped */
/* rows resolve their colour through --falcon-table-striped-color, so remapping */
/* only the base token would leave every second row failing (measured: 38 of 90 */
/* cells still at 3.22:1). */
[data-bs-theme="dark"] table {
  --falcon-table-color: var(--falcon-body-color);
  --falcon-table-striped-color: var(--falcon-body-color);
}

/* The navigation row (hamburger + logo + two dropdowns + theme toggle) overshoots the */
/* viewport in two narrow bands. Measured with both size rules forced on and off across */
/* 320-768 px, the row needs the reduced toggle below 340 px and again between */
/* 576 px and 620 px, where Bootstrap's sm breakpoint widens me-sm-3 and reveals the */
/* dropdown labels. Everywhere else — including every common phone width from 360 px */
/* up — the toggle keeps its 44x44 px WCAG 2.5.5 (AAA) target. The two bands must be */
/* read together with the logo rule below: neither alone clears 320 px. */
/* The upper bound is 339.98px, not 340px: re-measured on 16.09.2026 with the rule */
/* forced off, the row overshoots by 6 px at 320 px and by 10 px at 576 px but by 0 px */
/* at 339, 340 and 341 px, so at exactly 340 px the toggle can keep its full target. */
/* That also closes a one-pixel disagreement: the two sticky-bar reserves above break */
/* at 339.98px, so at 340px the control used to shrink while the space reserved for the */
/* bar did not. The responsive matrix measures 320 and 375 and never saw it. */
@media (max-width: 339.98px), (min-width: 576px) and (max-width: 620px) {
  .pd-theme-toggle {
    min-width: 30px;
    min-height: 32px;
  }
}

/* Below 375 px the 180 px logo leaves too little room for the rest of the navigation */
/* row: measured overflow is 52 px at 320 px, 32 px at 340 px and 12 px at 360 px with */
/* the toggle at full size. Scaling the logo reclaims exactly that, and the aspect */
/* ratio is preserved (180/36 = 130/26 = 5) so no layout shift is introduced. The bound */
/* is 374.98 px rather than 356 px because at 360 px the reduced toggle alone no longer */
/* applies — the two rules cover overlapping, not identical, ranges. */
@media (max-width: 374.98px) {
  .navbar-top .navbar-brand img {
    width: 130px;
    height: auto;
  }
}

/* The hamburger's 24 px floor above costs the row four pixels, and in two narrow bands
   the row has none to give: measured with the floor in place and the brand held at its
   documented width, the page overflowed by 1 px at 375 px and by 4 px at 576 px, while
   320 px, 600 px and 620 px still fit. Those four pixels are taken from the wordmark
   rather than from a control, and by a fixed amount rather than by letting flexbox
   decide — with `min-width: 0; flex-shrink: 1` on the brand the algorithm overshot to
   153 px at 375 px and 136 px at 600 px, a visible 15-24 % shrink of the logo at the
   default font size, which contradicts the two widths the project deliberately picked
   (180 px, and 130 px below 375 px). 172 px keeps eight pixels of headroom and is a
   4 % step, which is not perceptible beside the 28 % step the 130 px rule already makes.
   `min-height` guards the link's own hit area at the 24 px of WCAG 2.2 SC 2.5.8 (AA).
   The image deliberately carries no `max-width: 100%`: that declaration removes the
   flex item's automatic minimum size, and the wordmark then shrank on its own at every
   tight width — measured 112 px at 320 px, 152 px at 375 px and 124 px at 640 px, none
   of which any rule asked for. Its width stays under the explicit control of the two
   band rules instead. */
.navbar-top .navbar-brand {
  min-height: 24px;
}

@media (min-width: 375px) and (max-width: 430px), (min-width: 576px) and (max-width: 620px) {
  .navbar-top .navbar-brand img {
    width: 172px;
  }
}

/* Stop spinning animations for users who prefer reduced motion (WCAG 2.3.3). */
/* FontAwesome's .fa-spin runs as a CSS animation; on long-running pages like the */
/* thanks page countdown (up to 60s), the endless rotation can trigger vestibular */
/* discomfort. Suppressing it leaves the cog icon visible but stationary, which */
/* still communicates "in progress" alongside the live-region countdown text. */
@media (prefers-reduced-motion: reduce) {
  .fa-spin {
    animation: none !important;
  }
}

/* Legal pages: improved vertical rhythm for long-form text on mobile. */
/* Mirrors the body.is-blog-article pattern but with tighter spacing for legal text. */
/* Long paragraphs and ordered lists in tos.html and privacy.html otherwise render */
/* as walls of text on narrow viewports because every <p> uses mb-0 by convention. */
body.is-legal-page .card-body > p.fs-10 + p.fs-10 {
  margin-top: 0.75rem;
}

body.is-legal-page .card-body > h3 {
  margin-top: 1.5rem;
}

body.is-legal-page .card-body > ul.fs-10,
body.is-legal-page .card-body > ol.fs-10 {
  margin-bottom: 1rem;
}

body.is-legal-page .card-body > hr {
  margin-top: 1rem;
  margin-bottom: 1rem;
  opacity: 0.15;
}

/* Same reading measure as the articles, same reasoning — see the block above. The
   legal pages are the second body of long-form text this project ships, and the one
   a reader is least willing to work through. */
body.is-legal-page .card-body > p.fs-10,
body.is-legal-page .card-body > ul.fs-10,
body.is-legal-page .card-body > ol.fs-10,
body.is-legal-page .card-body > h3 {
  max-width: 68ch;
}

/* Stats cards on home.html: reduce minimum width so two columns fit comfortably on */
/* phones (375px viewport / 2 columns = ~180px effective width). The previous 12rem */
/* (192px) caused horizontal overflow on the smallest supported viewport. */
.pd-stat-card-min {
  min-width: 10rem;
}

/* Below the sm breakpoint the article rows in the category overviews cannot hold title
   and category badge side by side: at 320 px the title column measures ~106 px, less
   than a single long word, and the badge was pushed 51 px past the viewport. Letting
   the row wrap moves the badge under the title and gives the title the full line;
   min-width: 0 additionally allows the column to shrink below its min-content width,
   and hyphens: auto prefers proper German hyphenation over a mid-word break (the
   document is lang="de", so Chromium and Firefox apply real hyphenation patterns).

   All three declarations are scoped to this range on purpose. Applied unconditionally,
   min-width: 0 also shrank the two-column layout at 768-900 px, where the row fits at
   full width — it introduced word breaks there instead of preventing them. */
/* Links inside a table cell are the same defect as the article titles below, and
   they were reported twice before without being fixed: the internal cross-links in
   the comparison tables of the articles (329 tables) and the unlock links in the
   rating tables of the two analysis reports. Measured at 18 px high, and where the
   link is the whole content of its cell - which it is in these tables, the cell text
   and the link text are the same length - the inline exception of WCAG 2.2 SC 2.5.8
   (AA) does not cover it.
   The same mechanism as below: vertical padding on an inline element grows its hit
   box without growing its line box. Verified across three surfaces at 375 and
   1440 px that row heights, table heights and page height are byte-identical before
   and after, so the rule may match generously - a cell that holds text AND a link
   is unaffected either way, which is why CSS does not need to express "sole content
   of the cell" here. Minimum height 18 -> 26 px, count below 24 px: 18 -> 0. */
.card-body .table td a:not(.btn),
.card-body .table th a:not(.btn) {
  padding: 0.25rem 0;
}

/* The article title is the primary navigation target of the three category
   overviews, 140 rows in all, and it is the only content of its <h2> — so the
   inline exception of WCAG 2.2 SC 2.5.8 (AA) does not apply to it. Measured at
   375 and 1440 px it renders 23.0 px high: the inline box follows Open Sans's own
   ascent-plus-descent (1.4375 em at 16 px), which is why raising the h2's
   line-height would not move it. Vertical padding on the inline anchor lifts it to
   31 px and moves nothing else, because padding on an inline element does not grow
   its line box and the anchor carries no background. This mirrors what
   .footer-brand above does for the identical defect, with the same 0.25rem.
   Titles that wrap to two lines already measured 42 px and are unaffected. */
.pd-overview-row > h2 > a {
  padding: 0.25rem 0;
}

/* The icon column of each article row. This was an inline style repeated verbatim in
   all three overview templates, inside the article loop — 70 + 42 + 28 attributes,
   140 of the 146 inline styles measured across the whole site, and a width to change
   in three places. The parent already carried .pd-overview-row, so the class was the
   only thing missing. */
.pd-overview-row > .pd-overview-icon {
  width: 32px;
  text-align: center;
}

@media (max-width: 575.98px) {
  .pd-overview-row {
    flex-wrap: wrap;
  }

  .pd-overview-row > h2.flex-grow-1 {
    min-width: 0;
    flex-basis: 100%;
    hyphens: auto;
  }
}

/* At 320 px a col-6 is only ~146 px wide, so the 10rem (160 px) floor pushed the two
   stat cards 8 px past the viewport and produced the last remaining horizontal scroll
   bar on the site. Below 400 px the column width is the sensible constraint; the cards
   stay readable because their text is already set at the small fs-9 scale. */
@media (max-width: 400px) {
  .pd-stat-card-min {
    min-width: 0;
  }
}

/* Circular icon tiles on the blog overview, the home product cards, the related-  */
/* articles cards and the thanks page. Three fixed sizes, previously repeated as    */
/* nine inline width/height pairs. Falcon's own .icon-item scale does not carry     */
/* these values (sm 30px, lg 44px, xl 50px), so pulling them onto it would resize   */
/* every tile visibly for no stated reason — the values stay, only the inline style */
/* goes. Named after the pixel size like .pd-card-min-12 names its rem floor.       */
.pd-icon-tile-40,
.pd-icon-tile-56,
.pd-icon-tile-64 {
  flex-shrink: 0;
}

.pd-icon-tile-40 {
  width: 40px;
  height: 40px;
}

.pd-icon-tile-56 {
  width: 56px;
  height: 56px;
}

.pd-icon-tile-64 {
  width: 64px;
  height: 64px;
}

/* Corner-illustration cards in the steps, privacy and reporting section blocks. The */
/* 12rem floor keeps the illustration from crowding the text; these cards are never  */
/* narrower than a full column on phones, so unlike .pd-stat-card-min they do not    */
/* need the reduced 10rem width. Declared once here instead of twelve inline styles. */
.pd-card-min-12 {
  min-width: 12rem;
}

/* WebKit propagates a <select>'s layout overflow to its ancestors: the closed control
   is laid out at its own width (303 px here) but the widest <option> — "10 oder weniger
   Mitarbeiter (Teilzeit anteilig)" on the Kündigungsanalyse form — still contributes to
   the scrollable area, which travels up through .form-floating, .card-body and .content
   until the document itself scrolls. Measured on the JTA form: 14 px of horizontal page
   scroll at 320 px and 29 px at 375 px in WebKit, 0 px in Chromium and Gecko, confirmed
   with a real window.scrollTo(400, 0) reporting scrollX = 29. Since every browser on iOS
   uses WebKit, that is every iPhone visitor of that form.
   Clipping the select itself is the narrowest fix that works: overflow never affects an
   element's own outline, so the focus ring stays intact, and the dropdown popup is drawn
   by the operating system and is likewise unaffected. Applying the same clip one level up
   on .form-floating also removes the overflow but would clip the focus ring of the field
   inside it, trading a layout defect for a WCAG 2.4.7 defect. */
select.form-select {
  overflow: hidden;
}

/* Badges on the light brand fills carry dark text instead of white. White on        */
/* --falcon-success (#00d27a) measures 2.0:1, on --falcon-info (#27bcfd) 2.17:1 and  */
/* on --falcon-warning (#f5803e) 2.61:1 — all far below the 4.5:1 that WCAG 2.2 SC   */
/* 1.4.3 (AA) requires, and none of them reaches the large-text exemption at the     */
/* badge font size. These are not decorative: they carry the grade legend of the paid*/
/* Arbeitszeugnisanalyse report and the Kündigungsschutzklage verdict, i.e. exactly  */
/* the values a reader must be able to read. The brand colours themselves are        */
/* untouched — only the text on top changes, which lifts all three above 7:1.        */
.badge.bg-success,
.badge.bg-info,
.badge.bg-warning {
  color: var(--pd-ink-on-brand-fill);
}

/* The same correction for the circular icon tiles of the alert boxes, which the     */
/* badge rule above did not reach. Measured in the browser at 1440x900 with every    */
/* layer composited: the white glyph sits at 2.17:1 on --falcon-info and 2.61:1 on   */
/* --falcon-warning, in both themes — below the 3:1 that WCAG 2.2 SC 1.4.11 requires */
/* of a non-text element carrying meaning. These glyphs do carry it: they are the    */
/* success/warning/danger signal of the grade alert on the paid report pages, and    */
/* the same tile shows the hourglass of the 24-hour expiry banner. .bg-primary and   */
/* .bg-danger are deliberately not listed — they already measure 4.60:1 and 4.56:1   */
/* through the filled-variant correction CLAUDE.md records, and their glyphs stay    */
/* white. The 19 templates concerned had their .text-white class removed rather than */
/* this rule carrying !important: Bootstrap's utility is itself !important, so        */
/* overriding it would mean adding one more of the fifteen the project already wants */
/* to be rid of. Nothing new is introduced — --pd-ink-on-brand-fill exists for       */
/* exactly "the light brand fills (info, warning, success)".                         */
.bg-success.icon-item > .fas,
.bg-info.icon-item > .fas,
.bg-warning.icon-item > .fas,
.bg-success.icon-item > .fab,
.bg-info.icon-item > .fab,
.bg-warning.icon-item > .fab {
  color: var(--pd-ink-on-brand-fill);
}

/* Minimal darkening of three Falcon text-emphasis tokens so the alert copy clears
   WCAG 2.2 SC 1.4.3 (AA) on its own tint. Measured in light mode, where these tints
   are used: #00894f on #d9f8eb was 3.96:1, #197aa4 on #d8f3ff 4.17:1, #ac5a2b on
   #fde6d8 4.11:1. Hue and saturation are unchanged; only lightness drops by 7.7 %,
   4.6 % and 5.6 % respectively, which is the smallest step that reaches 4.5:1. The
   danger pair (#b82c46 on #fcdfe4) already measures 4.82:1 and is left alone.
   Scoped to light mode because Falcon gives these tokens different, already-passing
   values on the dark surface. The alerts carry the grade verdict of the paid reports,
   so this is exactly the text a reader must be able to read. */
:root:not([data-bs-theme="dark"]) {
  --falcon-success-text-emphasis: #007e49;
  --falcon-info-text-emphasis: #18749c;
  --falcon-warning-text-emphasis: #a25529;

  /* Falcon's code colour is a light pink that only works on the dark surface: it
     measures 6.57:1 there but 2.74:1 on white. <code> is not decoration in this project
     — it carries the reference id on pages/failure.html and in the payment
     reconciliation notice, the one string a customer whose payment is stuck has to read
     off the screen and quote to support. #d63384 is Bootstrap's own code colour and
     measures 4.50:1 on white; the dark theme keeps the original, which already passes. */
  --falcon-code-color: #d63384;
}

/* White on the neutral secondary fill measures 3.95:1 — below AA at the size these two
   buttons use (the "Originaltext anzeigen" control on the two analysis reports and the
   home button of the API failure page). Dark ink on the unchanged fill reaches 4.55:1.
   Darkening the fill instead would reach the same figure, but it would introduce a
   colour the project does not otherwise contain, and the inventory gate exists to stop
   exactly that. This way nothing is added: the fill stays --falcon-secondary and the ink
   is the same token the badges and the two brand-filled buttons already use. */
.btn-secondary {
  --falcon-btn-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-hover-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-active-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-disabled-color: var(--pd-ink-on-brand-fill);
}

/* Three brand fills were also being used as ink. Measured as rendered text on the light
   surfaces they actually sit on:
     .text-info    #27bcfd  1.88 - 2.17:1   (the "<60 Sek." figure on the homepage, the
                                             Arbeitszeugniserstellung headings, and the
                                             heading of the paywall overlay on its report)
     .text-success #00d27a  2.00:1
     .text-warning #f5803e  2.50 - 2.61:1   (including the severance amount on the
                                             Kündigungsanalyse report, set at 47.8 px —
                                             the single number that product exists to show)
   None of these reaches even the 3:1 that WCAG 2.2 SC 1.4.3 (AA) grants large text.
   Falcon already ships the ink variant of each hue as `--falcon-<name>-text-emphasis`,
   which the alert copy uses; pointing the text utilities at it lifts them to 4.58-5.43:1
   on every light surface while keeping the hue and therefore the product colour coding.
   The fills stay untouched, so a badge, a button and a heading of the same product still
   read as the same colour. The tokens invert with the theme, so the dark surface keeps
   its light ink automatically. Verified that none of the three classes sits on a dark
   fill anywhere: the only hits were icons on an 8 % tint, which composites to near-white.
   .text-primary and .text-danger are deliberately NOT remapped in THIS rule — at
   3.30-4.15:1 they were a borderline miss on the two most identity-defining hues, and
   that was the owner's call.
   RESOLVED, do not read this as still open: the owner decided on 20.08.2026, and both
   are remapped further down this same file — `.text-primary` for the light theme at the
   `[data-bs-theme="light"]` rule and for the dark theme at its counterpart, `.text-danger`
   at its own rule near the end. Only the passage above is historical. */
.text-info {
  color: var(--falcon-info-text-emphasis) !important;
}

.text-success {
  color: var(--falcon-success-text-emphasis) !important;
}

.text-warning {
  color: var(--falcon-warning-text-emphasis) !important;
}

/* The same correction for the filled buttons on those two brand colours. The rule
   above was applied to badges only, so on every page the identical fill carried dark
   text as a badge and white text as a button — and the button failed WCAG 2.2 SC 1.4.3
   (AA) in each of its four states. Measured against the Falcon state variables:

     .btn-info      rest #27bcfd  hover #21a0d7  active #1f96ca
                    white  2.17     2.97           3.35    -> all below 4.5:1
                    dark   8.32     6.07           5.38    -> all above
     .btn-warning   rest #f5803e  hover #d06d35  active #c46632
                    white  2.61     3.55           3.97    -> all below
                    dark   6.89     5.08           4.54    -> all above

   These are not decorative buttons: .btn-info is the "Erstellungen" control in the top
   navigation of every page and every Arbeitszeugniserstellung CTA, .btn-warning carries
   the Kündigungsanalyse. The brand fills themselves stay untouched — only the ink on top
   changes, exactly as for the badges.
   .btn-primary is deliberately NOT in this list: white measures 4.14 / 5.40 / 6.00 and
   dark 4.35 / 3.33 / 3.00, so neither ink clears 4.5:1 in every state. That one can only
   be resolved by adjusting the brand colour itself and is the owner's decision.
   .btn-danger is the same case and is left out for the same reason:
     .btn-danger    rest #e63757  hover #c42f4a  active #b82c46
                    white  4.16     5.45           6.02
                    dark   4.33     3.30           2.99
   White fails only at rest and dark fails everywhere else, so swapping the ink would
   trade one failing state for two. It carries the "Erkannte Stellen und Formulierungs-
   alternativen freischalten" CTA under the grade-4 findings on both analysis reports —
   the second-most important purchase control of the two paid analysis products — so
   this is not a decorative shortfall. Like .btn-primary it needs the brand red itself
   to change, which is an identity decision and therefore the owner's.
   RESOLVED, do not read this as still open: the owner made that decision on 20.08.2026.
   The two filled surfaces are corrected at the end of this file - `.btn-primary` to
   #1d72e3 (4.60:1) and `.btn-danger` to #e32346 (4.56:1), same hue, lightness down 3.2
   and 4.5 points. `--falcon-primary` and `--falcon-danger` keep the brand value; only the
   filled surfaces that carry white text moved. The analysis above is the record of how
   that decision came about, not of an open question. */
.btn-info,
.btn-warning {
  --falcon-btn-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-hover-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-active-color: var(--pd-ink-on-brand-fill);
  --falcon-btn-disabled-color: var(--pd-ink-on-brand-fill);
}

/* The same failure in the soft button variants, which the rule above does not reach:
   they draw their ink from a different variable and sit on --falcon-quaternary-bg
   (#ffffff in light mode) rather than on the brand fill. Measured there:
   #f5803e on #ffffff = 2.61:1 (2.50:1 on the tertiary surface #f9fafd) and
   #27bcfd on #ffffff = 2.17:1, against the 4.5:1 that SC 1.4.3 (AA) requires.
   These are not decorative either: .btn-falcon-warning is the "Übersicht" control at
   the head of all 30 Kündigung articles, .btn-falcon-info the Arbeitszeugniserstellung
   CTA in includes/jobreference.html. Dark mode already passes (6.89:1 / 8.32:1), which
   is why the gap survives development unnoticed.

   The ink is NOT set to --pd-ink-on-brand-fill here, which the filled buttons use: on a
   white ground that near-black would pass at ~16:1 but would erase the product colour
   the soft button exists to carry — every variant would look alike. It reuses the two
   text-emphasis tokens this file already darkened for the alert copy instead, so no new
   colour enters the palette and the button stays recognisably warning/info:
   #a25529 on #ffffff = 5.43:1 (5.20:1 on #f9fafd), #18749c = 5.23:1 (5.01:1). */
.btn-falcon-warning {
  --falcon-btn-falcon-warning-color: var(--falcon-warning-text-emphasis);
  --falcon-btn-falcon-warning-hover-color: var(--falcon-warning-text-emphasis);
  --falcon-btn-falcon-warning-active-color: var(--falcon-warning-text-emphasis);
}

.btn-falcon-info {
  --falcon-btn-falcon-info-color: var(--falcon-info-text-emphasis);
  --falcon-btn-falcon-info-hover-color: var(--falcon-info-text-emphasis);
  --falcon-btn-falcon-info-active-color: var(--falcon-info-text-emphasis);
}

/*
  Text-level contrast of the brand colour, light theme only.

  `--falcon-primary` (#2c7be5) reaches only 4.14:1 on white, while WCAG 2.2 AA requires
  4.5:1 for normal text. Measured across 942 nodes: every in-body link of the blog
  articles, every FAQ heading and the brand name in the footer. This rule fixes the
  `.text-primary` share of that. Body links draw their colour from the compiled theme
  through a different route: Falcon's `a` rule reads `--falcon-link-color-rgb`, which the
  rule below this one sets for the light theme and its dark counterpart sets for the dark
  one. Both are closed. (An earlier version of this comment pointed at a file
  `befunde/K21.md`, which does not exist in this repository - that was the working
  directory of a finished run and is gone.)

  #2569c3 is not a new colour — it is Falcon's own primary hover tone and already lives
  in this file as `--badge-primary-hover`. It raises body links and `.text-primary` to
  5.40:1 and is indistinguishable from the brand colour at 13px, so filled surfaces
  (buttons, badges, banners) keep the exact brand value and stay untouched.

  The `[data-bs-theme="light"]` scope is load-bearing, not decoration. On the dark
  background the same substitution moves the ratio the wrong way — measured 4.06:1 → 3.11:1
  for body links and 3.32:1 → 2.54:1 for `.text-primary`. Dark needs a *lighter* tone,
  which no existing project value supplies (`--falcon-info` is semantically bound to the
  Arbeitszeugniserstellung), so that half stays open rather than being made worse here.

  `!important` on `.text-primary` is required because the Bootstrap utility it overrides
  carries `!important` itself.
*/
/* Written as `:root:not([data-bs-theme="dark"])` for the reason given at the two
   light token blocks below: with JavaScript disabled the IIFE in `base.html` never
   writes the attribute, and this rule then did not apply — measured in Chromium with
   no attribute set, `.text-primary` rendered rgb(44, 123, 229), the uncorrected brand
   value at 4.14:1, while the body links beside it were already corrected. The scope
   still has to exclude the dark theme, where the same substitution moves the ratio
   the wrong way (3.32:1 → 2.54:1), and `:not([data-bs-theme="dark"])` does that. */
:root:not([data-bs-theme="dark"]) .text-primary {
  color: var(--badge-primary-hover) !important;
}

/*
  Body links, light theme only, same tone and same reason as above.

  Falcon's `a` rule reads `--falcon-link-color-rgb`, not `--falcon-link-color`, so setting
  the hex variable has no effect at all — measured: the link stayed at #2c7be5 / 4.14:1.
  37, 105, 195 is #2569c3 in the channel form that rule expects, which lifts every in-body
  link of the 143 articles to 5.40:1.

  Hover is deliberately left alone: it is a transient state, not the one a reader spends
  their time on, and Falcon derives it from its own variable.
*/
/* `:root:not([data-bs-theme="dark"])` rather than `[data-bs-theme="light"]`, and the
   difference is not stylistic: the attribute is written by the IIFE in `base.html`, so
   with JavaScript disabled there is no attribute at all. The light corrections a few
   hundred lines above already use this form and therefore applied in that state, while
   this one did not — the page then rendered with half of the light token set, links at
   4.14:1 among values that were corrected. */
:root:not([data-bs-theme="dark"]) {
  --falcon-link-color-rgb: 37, 105, 195;
}

/*
  Soft buttons: text colour of the primary and danger variants.

  `.btn-falcon-primary` paints its label in `--falcon-primary` (#2c7be5), which reaches
  4.14:1 on white where WCAG 2.2 AA (1.4.3) asks for 4.5:1 at 16px/500 — measured on
  nine nodes across the blog overview, the JRA/JCA formulars and both analysis reports.
  `.btn-falcon-danger` has the same defect at 4.16:1 with `--falcon-danger` (#e63757),
  on the "Bericht deaktivieren" control of every report page.

  The substitution is the project's own pattern, not a new decision: the
  `.btn-falcon-info` rule a few lines above already swaps in Falcon's own
  `*-text-emphasis` tone for exactly this reason. That variable is theme-aware, so one
  rule fixes both themes — light #215cac (6.58:1) / #b82c46 (6.02:1), dark #abcaf5
  (10.72:1) / #f4a5b3 (9.33:1). No new colour value enters the project and the filled
  brand surfaces stay untouched.
*/
.btn-falcon-primary {
  --falcon-btn-falcon-primary-color: var(--falcon-primary-text-emphasis);
  --falcon-btn-falcon-primary-hover-color: var(--falcon-primary-text-emphasis);
  --falcon-btn-falcon-primary-active-color: var(--falcon-primary-text-emphasis);
}

.btn-falcon-danger {
  --falcon-btn-falcon-danger-color: var(--falcon-danger-text-emphasis);
  --falcon-btn-falcon-danger-hover-color: var(--falcon-danger-text-emphasis);
  --falcon-btn-falcon-danger-active-color: var(--falcon-danger-text-emphasis);
}

/*
  Brand text on the light-theme brand tint.

  `.text-primary` sits at #2569c3 since the light-theme correction above, which clears
  4.5:1 on white but only reaches 4.31:1 on `--falcon-primary-bg-subtle` (#d9e7fa) —
  the rating badge and the reviewer initials of the JTA formular, ten nodes. The tint is
  a derived value, not a brand colour, so it is the part that moves: 20 % towards white
  lifts the pair to 4.53:1 and stays visually a pale brand blue.
*/
/* Same reasoning as the link-colour rule above: written so it also holds when no
   theme attribute is present. The two light blocks stay apart rather than being
   merged, because each carries the measurement that justifies it and moving the
   declarations away from their comment is what makes such a value look arbitrary
   later. */
:root:not([data-bs-theme="dark"]) {
  --falcon-primary-bg-subtle: #e1ecfb;
}

/*
  Secondary badges.

  White on `--falcon-secondary` (#748194) reaches 3.95:1. The badge carries "Nach
  Freischaltung" and the masked "?" of an unpaid report — the two places where a
  prospective buyer reads what is being withheld, so it is exactly the label that has to
  be legible. #697787 lifts it to 4.58:1. `--falcon-secondary` is a Falcon neutral and
  does not appear in the brand table, and the override is scoped to the badge so no
  other surface moves with it.
*/
.badge.bg-secondary {
  background-color: #697787 !important;
}

/*
  Range slider focus, JRC rating controls.

  Bootstrap draws the focused thumb with a translucent halo (rgba(44,123,229,.25),
  about 1.3:1 against white), which is the weakest focus indicator in the project while
  the three sliders it belongs to decide the grades of the certificate the visitor pays
  for. The ring below reuses the same 2px `--falcon-primary` the project already puts on
  every other control; the inner ring in the body colour keeps it readable on the track.
  `outline` cannot be used here — it applies to the input box, not to the thumb.
*/
.form-range:focus-visible::-webkit-slider-thumb {
  box-shadow:
    0 0 0 2px var(--falcon-body-bg),
    0 0 0 4px var(--falcon-primary);
}

.form-range:focus-visible::-moz-range-thumb {
  box-shadow:
    0 0 0 2px var(--falcon-body-bg),
    0 0 0 4px var(--falcon-primary);
}

/*
  Brand text in the dark theme.

  This is the half the light-theme correction above deliberately left open: on dark
  backgrounds `--falcon-primary` reaches 4.06:1 on #121e2d, 3.88:1 on #162231 and
  3.12:1 inside the improvement block — 155 nodes, the largest single contrast defect in
  the project. It needs a *lighter* tone, and Falcon ships one: the theme's own
  `--falcon-primary-text-emphasis` resolves to #abcaf5 in dark mode and reaches
  9.56:1 to 10.72:1 on those same backgrounds. Same hue, no new colour value, and the
  filled brand surfaces are untouched — a dark-theme heading and a body link simply stop
  being painted in the tone meant for light backgrounds.

  Both rules are scoped to `[data-bs-theme="dark"]`, mirroring the light-theme block,
  because the same substitution moves the light theme the wrong way.
*/
[data-bs-theme="dark"] .text-primary {
  color: var(--falcon-primary-text-emphasis) !important;
}

/*
  The theme switch itself, dark theme. Falcon paints both of its labels with the raw
  --falcon-primary, which measures 2.54:1 against the dark surface rgb(52,64,80) —
  below the 3:1 that WCAG 2.2 SC 1.4.11 requires of a control, and this one sits in
  the header of every single page. The same substitution the rule above makes for
  .text-primary lifts it to 6.26:1, measured in the browser. #abcaf5 is not a new
  value: it is the dark theme's own link colour, set eight lines below. The light
  theme is untouched and already passes at 3.16:1.
*/
[data-bs-theme="dark"] .theme-control-toggle-label {
  color: var(--falcon-primary-text-emphasis);
}

/*
  Body links, dark theme. Falcon reads `--falcon-link-color-rgb`, not the hex variable,
  so the tone has to be written in channel form: 171, 202, 245 is #abcaf5.
*/
[data-bs-theme="dark"] {
  --falcon-link-color-rgb: 171, 202, 245;
}

/*
  Outline buttons and danger text — the two remaining text usages of a semantic colour.

  `.btn-outline-primary` paints its resting label in `--falcon-primary` (4.14:1 light,
  4.06:1 dark) and `.text-danger` paints it in `--falcon-danger` (3.99:1 on the tertiary
  card background). The second one carries the two sentences of the Kündigungsanalyse
  that state whether the § 4 KSchG deadline has run out — the most consequential text in
  the free product.

  Both take the same route as the soft buttons above: Falcon's own theme-aware
  `*-text-emphasis` tone, so no new colour value enters the project and both themes are
  covered by one rule. Filled surfaces, borders and hover states keep the exact brand
  value; only the resting label moves. Measured light 6.58:1 / 5.77:1, dark 10.72:1 /
  9.33:1.
*/
.btn-outline-primary {
  --falcon-btn-color: var(--falcon-primary-text-emphasis);
}

.text-danger {
  color: var(--falcon-danger-text-emphasis) !important;
}

/*
  Filled brand surfaces that carry white text — the owner's decision of 20.08.2026.

  White on `--falcon-primary` (#2c7be5) measures 4.14:1 and on `--falcon-danger`
  (#e63757) 4.16:1, against the 4.5:1 WCAG 2.2 AA (1.4.3) asks for at these sizes.
  Sixty places, among them every `.badge.bg-primary` of the article lists, the two
  header buttons, the "Auffällige Stelle N" badges of both analysis reports and the
  purchase button itself.

  The correction is the smallest one that clears the threshold: the hue is untouched
  (214.4° and 349.0°) and only the lightness drops, by 3.2 and 4.5 points respectively.
  Measured 4.14 → 4.60:1 and 4.16 → 4.56:1. Side by side the two blues are practically
  indistinguishable, which is the point — the owner picked this over the larger step to
  the already-approved #2569c3 (5.40:1).

  `--falcon-primary` and `--falcon-danger` themselves stay at the exact brand value:
  borders, focus rings, the soft button family and the tinted backgrounds all continue
  to read them. Only the surfaces that paint white text on top move, and they move
  through the same rgb variable Bootstrap's opacity utilities use, so
  `.bg-primary.bg-opacity-25` keeps working.
*/
.btn-primary {
  --falcon-btn-bg: #1d72e3;
  --falcon-btn-border-color: #1d72e3;
}

.btn-danger {
  --falcon-btn-bg: #e32346;
  --falcon-btn-border-color: #e32346;
}

.bg-primary {
  --falcon-primary-rgb: 29, 114, 227;
}

.bg-danger {
  --falcon-danger-rgb: 227, 35, 70;
}
