/* ============================================================================
   LiNK — what Tailwind utilities cannot express.

   ⚠ LAYERED (2026-08-06). Everything below sits in `@layer link-extras`, which
   Tailwind v4 orders AFTER its own utilities layer. Before this, the file was
   unlayered and therefore outranked EVERY utility regardless of specificity —
   which caused three real defects (a stretched hero image, a doubled hero
   height, and a logo that refused to resize).

   Unlayered CSS beating layered CSS is also the single most likely way to break
   a Drupal SDC: a component ships its own utilities and a stray global rule
   silently wins. Keep new rules inside the layer, and only declare what
   utilities genuinely cannot express.
   ============================================================================ */

@layer link-extras {
  /* --- Sticky-header safety (2.4.11: focus must never hide under the bar) --- */
  html { scroll-padding-top: calc(var(--header-h) + 16px); }

  /* --- Persistent dashboard section navigation -----------------------------
     The authenticated header stays at the top already. Keep the dashboard tab
     row directly beneath it so clicking an in-page section never strands the
     visitor far below the navigation. This is deliberately component-scoped:
     public-page navigation does not inherit dashboard behavior. */
  :root { --app-header-h: 74px; }

  .app-tabs {
    position: sticky;
    top: var(--app-header-h);
    z-index: 30;
    box-shadow: 0 3px 8px rgb(5 68 107 / 12%);
  }

  /* The compact authenticated header is 42px tall plus 24px vertical padding. */
  @media (max-width: 560px) { :root { --app-header-h: 66px; } }

  /* Staff pages place their tab row beneath the staff-utility row as well. */
  .admin-nav {
    position: sticky;
    top: 68px;
    z-index: 35;
  }
  .admin-nav + .app-tabs { top: 119px; }

  /* --- Focus visibility: never removed, only improved ---------------------- */
  :focus-visible {
    outline: 3px solid var(--color-blue);
    outline-offset: 2px;
    border-radius: 2px;
  }

  /* --- Image height distortion guard --------------------------------------
     Tailwind Preflight already sets `img { max-width:100%; height:auto }`, and it
     does so INSIDE its base layer, so utilities like `h-full` / `size-full` still
     win. Do NOT restate it here: this file is a plain stylesheet, so an unlayered
     `img{height:auto}` outranks every layered utility and silently stretches any
     object-cover image to its natural aspect ratio.

     For fixed-ratio images, pin the ratio on the element instead: */
  .img-cover { width: 100%; height: 100%; object-fit: cover; }

  /* --- Scroll reveal: self-completing ------------------------------------
     Ends visible with `forwards`, so a slow load, a screenshot, or the Drupal
     Canvas editor preview never catches a blank section. JS only adds nicety. */
  @keyframes link-rise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
  [data-reveal] {
    animation: link-rise .55s cubic-bezier(.22,.61,.36,1) both;
    animation-timeline: auto;
  }
  [data-reveal].is-in { animation-play-state: running; }

  @media (prefers-reduced-motion: reduce) {
    [data-reveal] { animation: none; opacity: 1; transform: none; }
    * { scroll-behavior: auto !important; }
  }

  /* --- Image slot: striped placeholder for real photography ---------------
     Drag-drop AND click/keyboard operable (2.5.7 — dragging is never the only
     way). The stripe is ::before art, which utilities can't express.

     ⚠ This file is UNLAYERED, so any property set here outranks every Tailwind
     utility. Only declare what utilities genuinely cannot express. Positioning
     and overflow come from utilities in the markup (`relative`/`absolute` +
     `overflow-hidden`) — setting `position` here silently pulled absolutely
     positioned hero slots back into flow. */
  .image-slot::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: repeating-linear-gradient(
      -45deg,
      var(--color-rule) 0 10px,
      var(--color-shell) 10px 20px
    );
    opacity: .85;
  }
  .image-slot.is-filled::before { display: none; }
  /* Scoped to img only — a blanket `> *` would override `absolute` on captions. */
  .image-slot > img { position: relative; }
  .image-slot.is-dragover { outline: 3px dashed var(--color-blue); outline-offset: -6px; }

  /* --- Leaflet overrides (map-panel) --------------------------------------
     Third-party widget; utilities can't reach inside its generated DOM. */
  .leaflet-container { font-family: var(--font-body); background: var(--color-shell); }
  .leaflet-popup-content-wrapper { border-radius: 6px; }

  /* --- Body scroll lock while the mobile menu is open ---------------------- */
  body.menu-open { overflow: hidden; }

  /* --- Header logo: overhangs at rest, tucks in once scrolled ---------------
     At the top of the page the 215x121 logo hangs below the ~53px bar, matching
     the live build. Once the page scrolls it shrinks to a height the bar contains
     and centres itself, so it never covers the content being scrolled past.

     The width lives ENTIRELY here — the img carries no width utility at all.
     Two earlier attempts failed: an unlayered override lost the cascade to
     `.w-[215px]`, and `w-[var(--logo-w)]` was never generated by the Tailwind
     browser build. With no competing utility there is nothing to lose to.
     78px x (280/500) = 43.7px tall, inside the ~45px content box. */
  [data-site-logo] {
    width: 215px;
    max-width: none;
    transition: width .22s ease;
  }
  @media (max-width: 980px) { [data-site-logo] { width: 150px; } }
  @media (max-width: 560px) { [data-site-logo] { width: 118px; } }

  /* Scrolled state swaps to the MARK-ONLY lockup rather than shrinking the full
     one. The full lockup's minimum size is 170px wide — below that its tagline
     drops under the 8px legibility floor (see designs/STYLE-GUIDE-ADDENDUM.md).
     The mark at 95px is 43px tall with a 28px "LiNK" cap: legible, and it fits
     the ~45px content box. display:none keeps exactly one accessible name. */
  [data-site-logo-mark] { display: none; width: 95px; max-width: none; }
  .site-header.is-scrolled [data-site-logo] { display: none; }
  .site-header.is-scrolled [data-site-logo-mark] { display: block; }
  .site-header.is-scrolled .site-logo { align-items: center; }

  @media (prefers-reduced-motion: reduce) {
    [data-site-logo] { transition: none; }
  }

  /* --- Image gate (homepage "Featured with Icons" band) --------------------
     Matches the live build's complex_image_gate: four columns share a stack of
     background layers, and hovering a column cross-fades ITS image in. Column 1's
     image is the resting state.

     Pure CSS via :has() — four paired hover/focus states is exactly the kind of
     thing utilities express badly, and it means no JS for a decorative effect.
     :focus-within is included so the effect is reachable by keyboard. */
  .image-gate__bg { opacity: 0; transition: opacity .55s ease; }
  .image-gate__bg--1 { opacity: 1; }

  .image-gate:has(.image-gate__item:hover) .image-gate__bg--1,
  .image-gate:has(.image-gate__item:focus-within) .image-gate__bg--1 { opacity: 0; }

  .image-gate:has(.image-gate__item--1:hover) .image-gate__bg--1,
  .image-gate:has(.image-gate__item--1:focus-within) .image-gate__bg--1,
  .image-gate:has(.image-gate__item--2:hover) .image-gate__bg--2,
  .image-gate:has(.image-gate__item--2:focus-within) .image-gate__bg--2,
  .image-gate:has(.image-gate__item--3:hover) .image-gate__bg--3,
  .image-gate:has(.image-gate__item--3:focus-within) .image-gate__bg--3,
  .image-gate:has(.image-gate__item--4:hover) .image-gate__bg--4,
  .image-gate:has(.image-gate__item--4:focus-within) .image-gate__bg--4 { opacity: 1; }

  @media (prefers-reduced-motion: reduce) {
    .image-gate__bg { transition: none; }
  }

  /* --- Favourite heart pop ------------------------------------------------
     Fires on each press. prefers-reduced-motion is handled by the global rule
     above, which disables every animation. */
  @keyframes fav-pop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.28); }
    100% { transform: scale(1); }
  }
  .is-popping svg { animation: fav-pop .28s ease-out; }

  /* --- Progress tracker chevrons (buyer dashboard) ------------------------
     clip-path is the one thing utilities can't express here. Segments overlap
     by the notch depth so the arrows interlock. */
  .progress-step {
    clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%, 14px 50%);
    margin-left: -14px;
  }
  .progress-step:first-child {
    clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%);
    margin-left: 0;
  }
  .progress-step:last-child {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 14px 50%);
  }

  /* --- Loan breakdown donut ------------------------------------------------
     A conic-gradient ring, driven by CSS custom properties that main.js sets.
     Simple geometry, not hand-drawn art. */
  .loan-donut {
    background: conic-gradient(
      var(--color-navy)      0 var(--seg-principal, 25%),
      var(--color-blue)      var(--seg-principal, 25%) var(--seg-interest, 60%),
      var(--color-navy-mid)  var(--seg-interest, 60%) var(--seg-tax, 80%),
      var(--color-rule)      var(--seg-tax, 80%) 100%
    );
    mask: radial-gradient(circle, transparent 54%, #000 55%);
  }

  /* --- Logo overhang clearance -------------------------------------------
     The public logo is 215x120 inside a ~53px bar, so it deliberately hangs
     ~67px below the header — that overhang is the garza.link look, and
     designs/STYLE-GUIDE-ADDENDUM.md ties the size to tagline legibility, so
     shrinking it is not the fix.

     index.html absorbs the overhang with a full-bleed hero photo. Every other
     public page starts its content flush at the header, so the logo lands on
     it: at 1100px the search h1 runs under the mark. Reserve the space on
     <main> instead.

     Why <main> and not the first section: sections carry their own py-*
     utilities, and this file is LAYERED — a padding rule here would lose to
     them. <main> has no competing utility, so it applies cleanly.

     The signed-in .app-header contains its logo (50px in a 75px bar) and needs
     none of this, which is why the selector is scoped to .site-header. */
  :root { --logo-overhang: 67px; }

  .site-header ~ main { padding-top: var(--logo-overhang); }

  /* The homepage hero is designed to sit under the mark. */
  main:has(> .hero-search:first-child) { padding-top: 0; }

  /* Below 980px the logo shrinks to 150px wide (~84px tall), and at 560px to
     118px (~66px), so the reserved space follows it down. */
  @media (max-width: 980px) { :root { --logo-overhang: 40px; } }
  @media (max-width: 560px) { :root { --logo-overhang: 24px; } }
}
