/* ============================================================
   PCG — Hero guilloché
   Loads after components.css / effects.css, since it supersedes the
   hero's ambient particle canvas.

   Everything here is decorative, and everything here is visible by
   default. js/pcg-hero-canvas.js only ever *upgrades* what this sheet
   already draws — it never uncovers hidden content, and it is never
   asked for a class before something can be seen.

   Two nested boxes, two masks. CSS masks compose multiplicatively down
   a subtree, so nesting is how you intersect them — a single element
   can only ever union its mask layers, which is the opposite of what a
   clearance needs.

     .pcg-guilloche        keep off the fixed nav      (every mode)
     .pcg-guilloche__plate keep off the text column

   An earlier draft had a third box, .pcg-guilloche__field, carrying a
   radial fade "solid out to the rim, gone by 1.45 radii". That sounds
   like defence in depth and was arithmetically a no-op: --g-r IS the
   rim, the outermost thing the script draws, and the gradient stayed
   fully opaque out to 1.015 radii, so it could only ever attenuate the
   empty ring beyond the ornament. Below 960px the seal is held off the
   type by measured geometry instead — the script pulls the rim back
   until the nearest rendered glyph is outside it — which leaves that
   box nothing to intersect, so it is gone. A rule that looks
   protective but isn't is worse than no rule, because the next person
   to move the seal will trust it.

   The nav clearance is not decoration. Below 960px the burger and the
   header call to action are the only navigation on the page, the bar
   is transparent at scroll-top, and gold hairlines behind a gold
   burger cost it its affordance. It is masked on the module root
   rather than on the plate so the static fallback engraving is held
   off the bar too.
   ============================================================ */

.pcg-guilloche{
  /* Seed geometry, in hero coordinates. The script overwrites all
     three with measured pixel values once it knows where the monogram
     sits; until then — and permanently, if the script never runs —
     they are what places the static engraving below. Each breakpoint
     seeds the placement the script will actually land on, so a slow
     script never moves the ornament across the hero. */
  --g-x:75%;
  --g-y:50%;
  --g-r:min(30vw,340px);
  /* How far down the hero the fixed bar reaches. The script measures
     the real bar and overwrites this; the default is the bar at its
     tallest, so the clearance is still right with scripting off. */
  --g-safe-top:83px;
  /* And how far the hero's own bottom edge reaches back up into the
     engraving. Zero in ringed mode, and that is arithmetic rather than
     taste: layout() clamps a contained seal to the room it actually
     has, so at desktop the rim never reaches the join in the first
     place and there is nothing to soften.

     In edge mode it does reach it, badly. The seal is struck at 93% of
     the hero's height with a rim of roughly a third of the short side,
     so measured at dpr 2 the rim runs 67px past the hero's last pixel
     row at 390x844, 111px at 540x900 and 144px at 640x900 — and the
     hero clips (components.css: .hero{overflow:hidden}). A field of
     hairlines that stops dead along a section join does not read as an
     ornament leaving the sheet, it reads as a badly cropped print.

     So the clearance is a fade, not a margin: the engraving is still
     allowed to run off the bottom of the sheet, it is just not allowed
     to be guillotined on the way out. Two soft stops rather than one,
     for the same reason the top clearance has two — a single stop on
     line-work this fine still shows the ramp as a band. Nothing here
     animates and nothing here hides content: the whole module is
     decorative and aria-hidden. */
  --g-safe-bottom:0px;

  position:absolute;
  inset:0;
  z-index:2;               /* over the veil and the grain, under .hero__inner */
  pointer-events:none;

  -webkit-mask-image:linear-gradient(180deg,
    transparent 0 var(--g-safe-top),
    rgba(0,0,0,.45) calc(var(--g-safe-top) + 22px),
    #000 calc(var(--g-safe-top) + 62px),
    #000 calc(100% - var(--g-safe-bottom)),
    rgba(0,0,0,.34) calc(100% - var(--g-safe-bottom) * .42),
    transparent 100%);
          mask-image:linear-gradient(180deg,
    transparent 0 var(--g-safe-top),
    rgba(0,0,0,.45) calc(var(--g-safe-top) + 22px),
    #000 calc(var(--g-safe-top) + 62px),
    #000 calc(100% - var(--g-safe-bottom)),
    rgba(0,0,0,.34) calc(100% - var(--g-safe-bottom) * .42),
    transparent 100%);

  /* Entrance. A self-limiting keyframe that always ends on opacity:1
     and asks nothing of any script — no class to wait for, nothing to
     get stuck on if something throws upstream. */
  opacity:1;
  animation:pcg-guilloche-strike 1.5s var(--ease,cubic-bezier(.22,1,.36,1)) .12s backwards;
}
@keyframes pcg-guilloche-strike{
  from{opacity:0}
  to{opacity:1}
}

/* ---------- Static engraving: the no-canvas floor ----------
   Concentric hairlines — the crudest possible guilloché, but a true
   one, and enough that the hero never shows a hole where the ornament
   belongs. Fades out once the live plate takes over. */
.pcg-guilloche::before{
  content:"";
  position:absolute;
  left:var(--g-x);
  top:var(--g-y);
  width:calc(var(--g-r) * 2);
  height:calc(var(--g-r) * 2);
  translate:-50% -50%;
  border-radius:50%;
  background:repeating-radial-gradient(circle at 50% 50%,
    rgba(201,161,74,.34) 0 1px,
    transparent 1px 12px);
  -webkit-mask-image:radial-gradient(circle at 50% 50%,
    transparent 0 46%,#000 57%,#000 88%,transparent 100%);
          mask-image:radial-gradient(circle at 50% 50%,
    transparent 0 46%,#000 57%,#000 88%,transparent 100%);
  opacity:.55;
  transition:opacity .9s var(--ease,cubic-bezier(.22,1,.36,1));
}
.pcg-guilloche[data-live="1"]::before{opacity:0}

/* ---------- The plate ----------
   The masks live on these wrappers rather than on the canvas, because
   the canvas is only as big as the seal's bounding box — the script
   sizes and positions it — while the fades have to be reckoned in
   hero coordinates. The wrappers also keep the masks off the plate
   line. */
.pcg-guilloche__plate{
  position:absolute;
  inset:0;
  overflow:hidden;
  /* The engraving dissolves into shadow across the text column, the
     way .hero__veil treats the background behind it. Where masks are
     unsupported the seal simply stays whole — still legible, just
     less atmospheric. */
  -webkit-mask-image:linear-gradient(90deg,
    transparent 0 22%,rgba(0,0,0,.18) 42%,#000 66%);
          mask-image:linear-gradient(90deg,
    transparent 0 22%,rgba(0,0,0,.18) 42%,#000 66%);
}
.pcg-guilloche__cv{
  position:absolute;
  /* left / top / width / height are written by the script */
  left:0;
  top:0;
}

/* ---------- No plate line ----------
   A specimen label used to sit under the rim, reading "Guilloché, op maat
   getekend" / "Guilloché, drawn to order", with a lit rule beside it and a
   radial cartouche washing the engraving back to black so 11.5px of body
   text stayed over 5:1 on the line-work.

   ALL OF IT IS GONE, and the CSS is worth a note because the craft in it
   was real and it still had to go. It named the decoration for the reader,
   in the one place on the site where a visitor is deciding whether we solve
   their problem, and it asked them to know a word the market never uses.
   Every clever thing about the rule — the inscribed ellipse that reaches
   transparency exactly at the box edge on all four sides, the percentage
   geometry that tracked two languages of different lengths — was work spent
   making a caption legible that should not have been there. The ornament
   either carries the craft on its own or it does not; a label saying it
   does is not the fix.

   Removing it took the @media (max-width:1099px) block with it. That block
   held exactly one declaration, .pcg-guilloche__mark{display:none}, because
   a caption beside a full-width headline had nowhere to go. With the
   caption gone the breakpoint had nothing to say. */

/* ---------- Retire the ambient particle field ----------
   js/pcg-fx.js now bails out of heroParticles() when this module is on
   the page, so the retired canvas is never created and never drawn.
   This rule is the belt to that braces: it keeps an older cached copy
   of pcg-fx.js from drifting specks across the seal. Scoped through
   :has() so removing the snippet brings the old drift straight back,
   and so this sheet never has to reach into another module's file. */
.hero:has(.pcg-guilloche) .hero__particles{display:none}

/* ---------- Seal hung off the edge ----------
   Below 960px the monogram is gone, the type runs the full width and
   the seal has nothing to ring. The script hangs it off the
   bottom-right corner — measured to clear every glyph in the hero in
   both languages, at every width in this range — and the seed below
   puts the static ghost in the same place so nothing jumps.
   0.323 = the script's rIn fraction (.17) times its rim ratio (1.9),
   i.e. the seal at its natural size. The script only ever shrinks from
   there, and only where a caption reaches into the corner, so the
   fallback is never smaller than what replaces it.

   BELOW 960PX THE SEAL OWNS THE BOTTOM-RIGHT CORNER AND NOTHING ELSE
   MAY SIT THERE. The seal's placement is written by the script as
   inline pixels, so it is the only thing in that corner that cannot be
   moved by a stylesheet — which makes it the thing everything else has
   to clear. js/pcg-whatsapp.js carries the other half of this rule and
   the measurements behind it; its bubble used to sit inside this rim at
   every width from 390 up.

   960px, not 959px: the boundary that actually flips the script into
   edge mode is .hero__mono{display:none} at components.css
   @media (max-width:960px). At exactly 960px the old 959px seed put the
   static ghost centre-right while the script put the live seal in the
   corner — a one-pixel-wide window where the ornament jumped across the
   hero as the script landed, which is the one thing the seed exists to
   prevent. */
@media (max-width:960px){
  .pcg-guilloche{
    --g-x:106%;
    --g-y:93%;
    --g-r:calc(min(100vw,100svh) * .323);
    /* Scales with the seal, because the seal scales with the short
       side: 38px at 390 (about three ring pitches), 49px at 540, 58px
       at 640, 84px from 933 up. Enough to dissolve the line-work into
       the join without eating the arc — the visible seal is roughly
       185px deep at 390, so this spends a fifth of it on the exit. */
    --g-safe-bottom:clamp(38px,9vw,84px);
  }
}

.pcg-guilloche[data-mode="edge"] .pcg-guilloche__plate{
  opacity:.8;
  /* Softens the plate's left cut edge. The geometry already keeps the
     ornament out of the text column here, so this fade is defence in
     depth rather than the thing doing the work. */
  -webkit-mask-image:linear-gradient(90deg,
    transparent 0 calc(var(--g-x) - var(--g-r) * 1.6),
    rgba(0,0,0,.55) calc(var(--g-x) - var(--g-r) * 1.25),
    #000 calc(var(--g-x) - var(--g-r) * .95));
          mask-image:linear-gradient(90deg,
    transparent 0 calc(var(--g-x) - var(--g-r) * 1.6),
    rgba(0,0,0,.55) calc(var(--g-x) - var(--g-r) * 1.25),
    #000 calc(var(--g-x) - var(--g-r) * .95));
}
@media (max-width:859px){
  .pcg-guilloche::before{opacity:.28}
}

/* ---------- Reduced motion ----------
   The script already renders a single static frame and never starts a
   loop. All that is left is the entrance, which is the one thing here
   that moves on its own. */
@media (prefers-reduced-motion:reduce){
  .pcg-guilloche{animation:none}
  .pcg-guilloche::before,
  .pcg-guilloche__plate{transition:none}
}

/* ---------- Forced colours ----------
   Decoration only; step out of the way entirely. */
@media (forced-colors:active){
  .pcg-guilloche{display:none}
}
