/* ==========================================================================
   FRONTGRADE  ·  fg2 ADMIN CONTEXT
   Rules that exist only so Drupal's own chrome stays usable on top of the
   redesigned pages. Nothing here is part of the design system: keeping it in
   its own file is what lets the ported stylesheets stay byte-identical to the
   source they came from.
   ========================================================================== */

/* THE SYSTEM MESSAGE SLOT SITS ABOVE THE HERO, NOT INSIDE IT. The site header
   is position: fixed, so anything at the top of the document flows underneath
   it. The slot therefore carries its own clearance equal to the header plus a
   gap, and an opaque surface, so a status or error message is legible instead
   of being painted over by the header or lost against the hero artwork.

   THE CLEARANCE IS THE HEADER'S WHOLE BOX, --header-offset, not the main row
   alone: the row is 56px but the header also carries a utility bar, a top
   padding and two hairlines, and tokens.css states the total for exactly this
   kind of consumer. Measuring from the row left the first line of a message
   under the header's lower edge.

   ------------------------------------------------------------------------
   THE REGION BEING NON-EMPTY IS NOT EVIDENCE OF A MESSAGE, AND THAT IS THE
   DEFECT THE THREE RULES BELOW CLOSE.

   Every template gated its clearance on the pre_content region: the header
   partial prints this strip on {% if page.pre_content %} and each page
   template printed a .fg-header-offset spacer on the negation of the same
   test, so exactly one of the two was meant to be in the document. Core's
   messages block always renders, and when a visitor has nothing to read it
   renders one hidden div carrying data-drupal-messages-fallback. The region
   is therefore ALWAYS truthy. Measured on the running site before this
   change: .fg-header-offset appeared on no page at any width, and every page
   was in fact cleared by this strip standing empty at 109px on a phone and
   128px on a desktop, both of which are the strip's own padding rather than
   any stated clearance. The spacer, and the token behind it, were dead.

   THE TEST IS THE RENDERED MESSAGE, NOT THE REGION. [data-drupal-messages] is
   the wrapper core's status-messages template draws around the messages it
   has, and the template is rendered at all only when there are some: with
   nothing to say the placeholder resolves to nothing and the block leaves the
   hidden fallback div behind on its own. It is also the attribute
   Drupal.Message writes onto that fallback div before it injects a message at
   runtime, so a message added by script is caught by the same test as one
   rendered by PHP.

   .messages IS NAMED AS WELL, AND IS NOT WHAT MATCHES HERE. Read off the
   running site on a real failed login, Drupal 11's core template emits
   <div role="contentinfo" aria-label="Error message"> and no class at all;
   the class is something Olivero and Claro add in preprocess and this theme
   does not. It is kept in the list so a future theme or core release that
   restores it is covered, and the attribute is what actually fires today.

   WHAT NOW HAPPENS, IN THE THREE STATES THAT MATTER:

     no message          the strip collapses to nothing and the page's own
                         .fg-header-offset spacer is the whole clearance, at
                         --header-offset / --header-offset-mobile exactly
     a message           the spacer stands down, the strip carries the
                         clearance and prints the message on an opaque
                         surface below the header
     admin toolbar       neither figure changes: the toolbar offsets the
                         fixed header itself through --fg-admin-offset-y
                         further down this file, and both the spacer and this
                         strip are in the normal flow underneath it

   A PAGE WITH NO SPACER KEEPS THE STRIP AS ITS CLEARANCE. The front page
   declares no .fg-header-offset, by design: its hero is full bleed and the
   transparent fixed header is meant to float over it. The collapse is
   therefore conditioned on the page carrying a spacer, so a page that
   declares no clearance of its own is left exactly as it renders today.

   WITHOUT :has() both rules are dropped and a page shows the spacer and the
   strip's full padding together: a larger gap above the content, and no
   message hidden underneath anything. That is the safe direction to fail in,
   and it is why the clearance is stated here as the default and removed by
   the conditional rather than the other way round. */
.fg2-system-messages {
  position: relative;
  z-index: 1;
  padding-top: calc(var(--header-offset, 92px) + var(--space-6, 24px));
  padding-bottom: var(--space-3, 12px);
  background: var(--surface-base, #0b0f14);
  color: var(--text-body, #d6dde5);
}

@media (max-width: 1024px) {
  .fg2-system-messages {
    padding-top: calc(var(--header-offset-mobile, 73px) + var(--space-6, 24px));
  }
}

/* Nothing to read: the strip is not in the way of anything. */
body:has(.fg-header-offset) .fg2-system-messages:not(:has([data-drupal-messages], .messages)) {
  padding: 0;
  background: none;
}

/* Something to read: the strip is the clearance and the spacer stands down,
   so the two are never counted twice. */
body:has(.fg2-system-messages :is([data-drupal-messages], .messages)) .fg-header-offset {
  display: none;
}

/* THE ADMIN TOOLBAR TAKES THE TOP OF THE VIEWPORT BEFORE THE SITE HEADER
   DOES, AND IT IS NOT ONE SHAPE. Core's classic bar, the Gin admin theme's
   horizontal bar plus the secondary row it adds on the front end, and Gin's
   vertical sidebar each occupy a different amount of the screen, and the
   sidebar claims the left edge as well as the top. Both toolbars publish the
   space they occupy as custom properties, so the header is offset by those
   values rather than by heights copied out of their stylesheets, and it stays
   clear as the toolbar changes shape underneath it: orientation switched,
   tray opened, sidebar expanded.

     --drupal-displace-offset-top   core's displace API, kept current as the
                                    classic tray opens and closes
     --gin-offset-y                 Gin's own total, its fixed bar plus the
                                    secondary row
     --gin-toolbar-x-offset         Gin's vertical sidebar, 66px collapsed
                                    and 256px expanded

   Each fallback is the value that applies when the property is absent, so a
   page rendered before the toolbar's own scripts report still lands clear of
   the chrome. Everything here is scoped to a toolbar-bearing session, so a
   visitor is never offset by chrome that is not on their page. */
body.toolbar-fixed {
  --fg-admin-offset-y: var(--drupal-displace-offset-top, 39px);
}

body.gin--horizontal-toolbar,
body.gin--vertical-toolbar {
  --fg-admin-offset-y: var(--gin-offset-y, 113px);
  --fg-admin-offset-x: var(--gin-toolbar-x-offset, 0px);
}

/* The header is anchored left AND right, so moving its left edge narrows it
   rather than pushing it off the screen. */
body.toolbar-fixed .fg-header,
body.gin--horizontal-toolbar .fg-header,
body.gin--vertical-toolbar .fg-header {
  top: var(--fg-admin-offset-y, 0px);
  left: var(--fg-admin-offset-x, 0px);
}

/* With the admin toolbar present the fixed header gives way: core's toolbar
   and its trays sit at z-index 501 and 502, below the header's own token
   value. Scoped to toolbar-bearing sessions, so visitors never see it. */
body.toolbar-fixed .fg-header,
body.gin--horizontal-toolbar .fg-header,
body.gin--vertical-toolbar .fg-header {
  z-index: 499;
}

/* Gin's secondary row belongs to the document rather than to the viewport, so
   it scrolls away and leaves the band it occupied showing page content
   between the fixed toolbar and the header. The header's own solid fill,
   which fades in on scroll and is a pseudo-element behind its contents, is
   extended up through that band so the two read as one bar once the row has
   gone. At the top of the page that fill is still transparent, so what shows
   through the band there is the row itself. */
body.gin--horizontal-toolbar .fg-header::before,
body.gin--vertical-toolbar .fg-header::before {
  top: calc(-1 * var(--gin-toolbar-secondary-height, 60px));
}


/* CORE'S OWN SKIP LINK. html.html.twig prints Drupal's `visually-hidden
   focusable` link as the first thing in the body, and core reveals it on
   focus by returning it to the normal flow. That puts it at the top of the
   document, which is underneath the fixed header, so the visitor it exists
   for cannot see what they are about to activate. It is given the same fixed
   placement, the same stacking level and the same treatment as the design's
   own skip link, offset below whatever admin chrome the session carries.
   Core sets `position: static !important` on this state, so the override
   carries the same weight.

   The rule is scoped to a direct child of the body, which is the skip link
   and nothing else: every other `visually-hidden focusable` control on the
   site is nested inside the page and keeps core's behaviour. */
body > a.visually-hidden.focusable:active,
body > a.visually-hidden.focusable:focus-within {
  position: fixed !important;
  top: calc(var(--fg-admin-offset-y, 0px) + var(--space-3));
  left: var(--space-3);
  z-index: var(--z-skip);
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: var(--space-3) var(--space-5);
  background: var(--accent);
  color: var(--text-on-accent);        /* 5.22:1 */
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
}
