/* PTR motion layer (operator 2026-09-25: "website needs to come to life and be fast / effects as you scroll /
 * micro interactions / rainbow glow circle button effect, Google CSS hover style / buttons need to feel
 * clickable without being cliche / header, footer and hamburger unchanged but can feel more alive").
 *
 * Rules this file keeps:
 * 1. NOTHING is hidden unless ptr-motion.js has put html.pm-on, and the script never does that under
 *    prefers-reduced-motion (the prerender capture runs with reduced motion, so no hidden state can ever
 *    reach a prerendered snapshot, a crawler or a no-JS visitor).
 * 2. Only opacity, transform and filter animate: compositor work, no layout, so no CLS.
 * 3. Header (.ptrNav*) and hamburger menu (.ptrMenu*) get hover life only: no layout, size, colour-at-rest or
 *    markup change. The footer group, Talk to Us and the enquiry form are not touched at all.
 * 4. Hover effects sit behind (hover: hover) so touch devices never get stuck hover states.
 *
 * ONE MOTION LANGUAGE (motion round 2026-09-26). Every transition on the site that this sheet owns now uses
 * one easing family (--pm-ease) and one of two durations: --pm-micro (180 ms) for anything that answers the
 * pointer (hover colour, ring, lift, press) and --pm-reveal (560 ms) for a band arriving on scroll. The
 * looping ambient effects (the ring's spin, the halo) are periods, not transitions, and keep one period each.
 * The shared ring is ONE class: .pm-ring draws the rainbow ring and halo and nothing else; .pm-btn and the
 * pill, the Simon's Twin dock tiles and their links add the lift. TopNav's controls carry .pm-ring instead of
 * a copy of these rules. */

@property --pm-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

:root {
  --pm-ease: cubic-bezier(.2, .72, .18, 1);
  --pm-micro: 180ms;
  --pm-reveal: 560ms;
  --pm-rise: 26px;
}

/* ---------- 1. Reveal on scroll ---------- */
/* One dominant element per band (ptr-motion.js decides): it rises into place ("up", or "media" for a grid of
   tiles, which also arrives desaturated). Everything else in the band only fades ("fade"): no second rise, so a
   band no longer arrives as a stack of identical lifts. Reveals replay in both directions: an element that has
   left the viewport by a wide margin is put back to its start state, facing the way it will come back in
   (data-pm-dir="down" when it waits ABOVE the viewport, so it settles downward into place). */
html.pm-on [data-pm] {
  opacity: 0;
  transform: translate3d(0, var(--pm-rise), 0);
  transition: opacity var(--pm-reveal) var(--pm-ease), transform var(--pm-reveal) var(--pm-ease), filter var(--pm-reveal) var(--pm-ease);
  transition-delay: var(--pm-delay, 0ms);
}
html.pm-on [data-pm][data-pm-dir="down"] { transform: translate3d(0, calc(-1 * var(--pm-rise)), 0); }
html.pm-on [data-pm="fade"], html.pm-on [data-pm="fade"][data-pm-dir="down"] { transform: none; }
/* No scale in any start state: a scaled box moves its top by more than --pm-rise, which the reveal margin in
   ptr-motion.js (RISE) must cover (Codex r3). Media arrives on the same rise, desaturated and softly blurred. */
html.pm-on [data-pm="media"] { filter: saturate(.6) blur(2px); }
html.pm-on [data-pm].pm-in { opacity: 1; transform: none; filter: none; }

/* Headings sweep their brand gradient once as they arrive. */
/* The gradient's resting position shows only its currentColor stop, so the finished heading reads exactly as
   before; the text fill goes transparent ONLY where text-clipped backgrounds are supported, so no browser can
   ever render the heading invisible. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  html.pm-on .ptrsx-h2.pm-sweep {
    background: linear-gradient(100deg, currentColor 0 38%, #A567F1 46%, #41BBF5 52%, currentColor 60% 100%) 0 0 / 260% 100% no-repeat;
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
}
html.pm-on .pm-in .ptrsx-h2.pm-sweep, html.pm-on .ptrsx-h2.pm-sweep.pm-go {
  animation: pm-sweep var(--pm-reveal) var(--pm-ease) 120ms both;
}
@keyframes pm-sweep { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* The page hero's one entrance: its media settles in on load (transform only: the poster still paints at first
   frame). The copy no longer rises line by line on top of it; one movement per band. */
html.pm-on .ptrsx-herofull__media > video,
html.pm-on .ptrsx-herofull__media > img { animation: pm-settle var(--pm-reveal) var(--pm-ease) both; }
@keyframes pm-settle { from { transform: scale(1.06); } to { transform: scale(1); } }

/* Scroll progress: a hairline of the brand gradient along the BOTTOM edge, beneath the header (800), the menu
   (890), the contact overlay (950) and the Didymo widget, so it never paints over a protected surface. */
#pm-progress {
  position: fixed; left: 0; bottom: 0; z-index: 700; height: 2px; width: 100%;
  background: linear-gradient(90deg, #A567F1, #41BBF5, #80EDEF);
  transform-origin: 0 50%; transform: scaleX(0); pointer-events: none; opacity: .85;
}

/* ---------- 2. Buttons: the shared ring ---------- */
/* Keyboard focus is ALWAYS a solid, high-contrast outline (motion or not); the glow is decoration on top. */
.ptrsx-pillbtn:focus-visible, .pm-btn:focus-visible, .pm-ring:focus-visible { outline: 2px solid #6D57FF; outline-offset: 3px; }
.ptrsx-pillbtn, .pm-btn, .pm-ring, .twinPhotoLink, .twinPhotoActions button { position: relative; }
/* The crisp ring: a conic rainbow masked to a 2px band on the control's edge. It sits ABOVE the control's own
   background (no negative z-index), so it shows on white sections, dark sections and glass CTA bands alike. */
.ptrsx-pillbtn::after, .pm-btn::after, .pm-ring::after, .twinPhotoLink::after, .twinPhotoActions button::after {
  content: ''; position: absolute; inset: -2px; padding: 2px; border-radius: inherit; pointer-events: none;
  /* The gradient is written HERE, not through a :root variable: a custom property that references --pm-angle is
     resolved once at :root (angle 0) and inherited as a fixed value, so spinning --pm-angle on the button never
     moved the ring (operator 2026-09-25: "the glow button effect does not animate"). */
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
/* The halo: the control's own shadow cycles through the same colours, so it glows outward without a
   pseudo-element that a section background could paint over. */
@keyframes pm-halo {
  0%, 100% { box-shadow: 0 0 0 1px rgba(66, 133, 244, .18), 0 0 24px 1px rgba(66, 133, 244, .42), 0 12px 28px rgba(11, 13, 18, .14); }
  25%      { box-shadow: 0 0 0 1px rgba(155, 114, 203, .18), 0 0 24px 1px rgba(155, 114, 203, .42), 0 12px 28px rgba(11, 13, 18, .14); }
  50%      { box-shadow: 0 0 0 1px rgba(217, 101, 112, .18), 0 0 24px 1px rgba(217, 101, 112, .40), 0 12px 28px rgba(11, 13, 18, .14); }
  75%      { box-shadow: 0 0 0 1px rgba(52, 168, 83, .18), 0 0 24px 1px rgba(65, 187, 245, .42), 0 12px 28px rgba(11, 13, 18, .14); }
}
@keyframes pm-spin { to { --pm-angle: 360deg; } }
/* A contact-form field's hover glow drifts through the same colours as the button ring. */
@keyframes pm-field-glow {
  0%, 100% { box-shadow: 0 0 0 1px rgba(66, 133, 244, .35), 0 8px 26px rgba(66, 133, 244, .16); }
  33%      { box-shadow: 0 0 0 1px rgba(155, 114, 203, .35), 0 8px 26px rgba(155, 114, 203, .16); }
  66%      { box-shadow: 0 0 0 1px rgba(65, 187, 245, .35), 0 8px 26px rgba(65, 187, 245, .16); }
}
/* Without motion the ring still appears on hover and focus, static and instant: the control still reads as
   clickable. */
@media (hover: hover) {
  .ptrsx-pillbtn:hover::after, .pm-btn:hover::after, .pm-ring:hover::after, .twinPhotoLink:hover::after, .twinPhotoActions button:hover::after { opacity: 1; }
}
.ptrsx-pillbtn:focus-visible::after, .pm-btn:focus-visible::after, .pm-ring:focus-visible::after, .twinPhotoLink:focus-visible::after { opacity: 1; }
/* A ring on a PART of a control (the hamburger's expander discs): the control carries .pm-ring-host, the disc
   .pm-ring, and hovering or focusing the whole control lights the disc. */
@media (hover: hover) { .pm-ring-host:hover .pm-ring::after { opacity: 1; } }
.pm-ring-host:focus-visible .pm-ring::after { opacity: 1; }

/* Clickable cards carry the same rainbow ring (operator 2026-09-25: "want the effect on clickable cards").
   OUTSIDE THE CARD (operator 2026-09-27 round 3b: "the stroke line covers text"): the ring used to sit just inside
   the edge (inset 0, above the media), so on a captioned card it ran over the caption's edge. It now sits 3px
   outside the edge at the card's radius plus 3px, concentric, and can never touch the card's content. A card that
   clipped its overflow would clip the ring, so the clipping moves to the media (the featured-work loops and the
   .ptrdr tiles round their own top corners); .ptrsx-srcrow keeps its ring inside because its stretched link
   relies on the card's overflow clip, and its copy sits 24px in from the edge. The radius comes from --card-r,
   which ptr-motion.js writes from each card's computed corner (border-radius:inherit cannot be added to, and a
   page may give a card any radius: Work's case-study cards are 24px), with --pm-r (20px here, 22px on Home's
   download cards) as the fallback before the script runs. */
/* A card carries the radius of its media (operator 2026-09-27: "the stroke on cards is bad shaped, they are square and
   cards are curved"): the ring below is border-radius:inherit, and a card with no radius of its own drew a square ring
   around a curved picture. A page's own, more specific card rule still wins where it sets one. */
.ptrsx-tilelink, .fwCard, .homeNewsCard, .homeFormatTile, .ptrsx-dlcard { position: relative; --pm-r: 20px; border-radius: var(--pm-r); }
.ptrdr .ptrsx-tile.ptrsx-tilelink { overflow: visible; }
.ptrdr .ptrsx-tile.ptrsx-tilelink > :first-child:is(img, video, picture, figure) { border-radius: 19px 19px 0 0; overflow: hidden; }
/* A GAP BETWEEN THE RING AND THE CARD (operator 2026-09-27 round 4: "you didnt fix cards stroke when hovered still
   covers cards", on Home's four ways cards). MEASURED at 1440: Home's cards carry no padding, so their captions and
   pictures run to the card's edge, and a ring 3px out left its 2px stroke 1px from the words ("Chat", "Healthcare
   and medical", "XR explained", every news headline) and from the picture. Home's cards now draw the ring 10px out
   (--pm-ring-out), concentric at their radius + 10, so the stroke sits 8px clear of every glyph and every picture.
   A card elsewhere keeps 3px unless it sets the variable. */
.homeFormatTile, .homeNewsCard, .fwCard, .homeDocs .ptrsx-dlcard { --pm-ring-out: 10px; }
.ptrsx-tilelink::after, .fwCard::after, .homeNewsCard::after, .homeFormatTile::after, .ptrsx-dlcard::after {
  content: ''; position: absolute; inset: calc(-1 * var(--pm-ring-out, 3px)); padding: 2px; border-radius: calc(var(--card-r, var(--pm-r, 20px)) + var(--pm-ring-out, 3px)); pointer-events: none; z-index: 4;
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
@media (hover: hover) {
  .ptrsx-tilelink:hover::after, .fwCard:hover::after, .homeNewsCard:hover::after, .homeFormatTile:hover::after, .ptrsx-dlcard:hover::after { opacity: 1; }
}
.ptrsx-tilelink:focus-visible::after, .fwCard:focus-visible::after, .homeNewsCard:focus-visible::after, .homeFormatTile:focus-visible::after { opacity: 1; }

/* SOURCE CARDS ARE TILES (operator 2026-09-27: "the sources are supposed to be hoverable clickable tiles throughout
   website"). A .ptrsx-srcrow used to answer the pointer with a 3px lift while only its title or its "Read it at"
   line was the link. Now the WHOLE card is the click target and wears the shared rotating ring on hover at its own
   radius (20px). ONE link per card is stretched over it: the title link where the card has one, otherwise the last
   line's last link (a card whose last line pairs an internal link with the source link stretches the source); every
   other link in the card sits above the stretch. The stretched pseudo spills far past the copy block (its
   containing block, which the desktop sheet positions) and the card's overflow clips it to the card, so the picture
   half of a side-by-side card is clickable too. THE RING IS THE ::after, as on every other card (the census gate
   reads ::after): where the desktop sheet had drawn the picture's scrim on ::after, the scrim moves to ::before
   (same gradients, same award variants) and the ring takes ::after with a more specific selector. Links inside
   read cyan: the card is dark. */
.ptrsx-srcrow { position: relative; overflow: hidden; border-radius: 20px; cursor: pointer; --pm-link: #80EDEF; --pm-link-hover: #A567F1; --pm-link-glow: rgba(128, 237, 239, .55); }
.ptrsx-srcrow .ptrsx-srccopy a { position: relative; z-index: 3; }
.ptrsx-srcrow .ptrsx-srccopy :is(h2, h3) > a:first-of-type,
.ptrsx-srcrow .ptrsx-srccopy:not(:has(:is(h2, h3) > a)) p:last-of-type > a:last-of-type { position: static; }
.ptrsx-srcrow .ptrsx-srccopy :is(h2, h3) > a:first-of-type::after,
.ptrsx-srcrow .ptrsx-srccopy:not(:has(:is(h2, h3) > a)) p:last-of-type > a:last-of-type::after { content: ''; position: absolute; inset: -100vh -100vw; z-index: 2; }
.ptrsx-srcrow::after {
  content: ''; position: absolute; inset: 0; padding: 2px; border-radius: inherit; pointer-events: none; z-index: 4;
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
/* The award list's even rows carry their own scrim selector at a higher specificity than the card rule, so the
   ring names them too (otherwise their ::after keeps the linear scrim and the hover shows a dark border). */
html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow::after,
html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow.ptrsx-srcrow:nth-child(n)::after {
  content: ''; position: absolute; inset: 0; padding: 2px; border-radius: inherit; pointer-events: none; z-index: 4;
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow::before { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(90deg, rgba(6,8,16,0.86) 0%, rgba(6,8,16,0.72) 55%, rgba(6,8,16,0.58) 100%); }
html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow::before, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow:nth-child(even)::before { background: linear-gradient(90deg, rgba(6,8,16,0.86) 0%, rgba(6,8,16,0.70) 28%, rgba(6,8,16,0.00) 45%); }
@media (max-width: 899.98px) {
  html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow::before, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow:nth-child(even)::before { background: linear-gradient(180deg, rgba(6,8,16,0.35) 0%, rgba(6,8,16,0.88) 100%); }
}
@media (hover: hover) { .ptrsx-srcrow:hover::after, html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow:hover::after, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow.ptrsx-srcrow:nth-child(n):hover::after { opacity: 1; } }
.ptrsx-srcrow:focus-within::after, html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow:focus-within::after, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow.ptrsx-srcrow:nth-child(n):focus-within::after { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .ptrsx-srcrow::after, html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow::after, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow.ptrsx-srcrow:nth-child(n)::after { transition: opacity var(--pm-micro) var(--pm-ease); }
  @media (hover: hover) { .ptrsx-srcrow:hover::after, html body .ptrdr .ptrcx-section .ptrsx-srcrow.ptrsx-srcrow:hover::after, html body .ptrdr .ptrcx-section .ptrsx-srclist--award > .ptrsx-srcrow.ptrsx-srcrow.ptrsx-srcrow:nth-child(n):hover::after { animation: pm-spin 2.6s linear infinite; } }
}

/* THE DOWNLOAD CARD IS CLICKABLE AS A WHOLE (census 2026-09-27): its title is a link (CaseStudyDownloads.dc.html,
   class ptrsx-dlstretch) stretched over the card; the cover link and the pill row sit above it. Its colour at rest is
   the card's own text colour (section 4, round 4); it takes the card-title gradient on hover. */

/* A CTA BAND INSIDE A CARD GRID (census 2026-09-27: on two industry pages the band was inserted as a grid item, so its
   100vw box lay across the neighbouring cell and the links there sat under its video): it spans the row. */
/* Specificity beats the desktop sheet's row-balancing rule (html body .ptrdr .ptrcx-section :is(...) > :nth-last-child(4)
   :nth-child(3n+1) ~ * { grid-column: span 3 !important }), which otherwise keeps the band at span 3. */
html body .ptrdr .ptrcx-section :is(.ptrcx-cards, .ptrcx-grid-2, .ptrcx-grid-3) > .ptrsx-ctaband.ptrsx-ctaband.ptrsx-tight,
html body .ptrdr :is(.ptrcx-cards, .ptrcx-grid-2, .ptrcx-grid-3) > .ptrsx-ctaband.ptrsx-ctaband.ptrsx-tight { grid-column: 1 / -1 !important; }

/* CARD TITLES TAKE THE HOVER COLOUR (operator 2026-09-27 round 3b: the bold titles "no longer change colour on
   hover"): when a card is hovered its title reads in the link gradient, like a header row: a caption's <strong>
   (Home's four program cards), an industry tile's figcaption, a news headline, a four ways h3, a featured-work
   "Explore the case study" line, a build card's name, a download card's title. An arrow inside keeps a solid fill,
   since a child cannot share its parent's clipped gradient. */
@media (hover: hover) {
  :is(.ptrsx-tilelink, .homeFormatTile, .homeNewsCard, .fwCard, .ptrsx-dlcard, .homeBuildType):hover :is(figcaption strong, .homeFourStory strong, h3, .fwMore, .homeBuildName, .ptrsx-dltitle),
  .homeFormatTile:hover > figure:last-child figcaption {
    color: var(--pm-link-hover, #A567F1); background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
  :is(.ptrsx-tilelink, .homeFormatTile, .homeNewsCard, .fwCard, .ptrsx-dlcard, .homeBuildType):hover :is(figcaption strong, .homeFourStory strong, h3, .fwMore, .homeBuildName, .ptrsx-dltitle) .homeArrow,
  .homeFormatTile:hover > figure:last-child figcaption .homeArrow { -webkit-text-fill-color: var(--pm-link-hover, #A567F1); }
}

/* Contact form fields (class pm-field, Contact.dc.html): a soft brand glow on hover. The component's own focus
   style (style-focus) still owns the focused state. */
.pm-field { transition: border-color var(--pm-micro) var(--pm-ease), box-shadow var(--pm-micro) var(--pm-ease); }
@media (hover: hover) {
  .pm-field:hover:not(:focus) { border-color: rgba(128, 237, 239, .55) !important; box-shadow: 0 0 0 1px rgba(128, 237, 239, .25), 0 8px 26px rgba(65, 187, 245, .16); }
}

/* The "What are you interested in" dropdown (operator 2026-09-25: "stylize the drop down menu in the contact
   form"). The form sets its background inline, so the chevron and spacing need !important to show. */
select.pm-field {
  -webkit-appearance: none !important; appearance: none !important; cursor: pointer;
  padding-right: 48px !important;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='%2380EDEF'/%3E%3Cstop offset='1' stop-color='%23A567F1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M1 1.5l6 6 6-6' fill='none' stroke='url(%23g)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
    linear-gradient(135deg, rgba(128, 237, 239, .10), rgba(165, 103, 241, .10)) !important;
  background-repeat: no-repeat, no-repeat !important;
  background-position: right 18px center, 0 0 !important;
  background-size: 14px 9px, 100% 100% !important;
  font-weight: 500;
}
select.pm-field option { background: #0B0D12; color: #fff; padding: 10px; }
/* Until a choice is made the list reads as a prompt (muted), not as a value. */
select.pm-field:invalid { color: rgba(255, 255, 255, .62) !important; }
select.pm-field { padding-left: 16px !important; letter-spacing: .01em; transition: border-color var(--pm-micro) var(--pm-ease), box-shadow var(--pm-micro) var(--pm-ease), background-color var(--pm-micro) var(--pm-ease); }
@media (hover: hover) { select.pm-field:hover:not(:focus) { background-color: rgba(128, 237, 239, .06) !important; } }
select.pm-field option:checked { background: #1B1E28; color: #80EDEF; }
select.pm-field:focus { background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 7.5l6-6 6 6' fill='none' stroke='%2380EDEF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"),
    linear-gradient(135deg, rgba(128, 237, 239, .14), rgba(165, 103, 241, .14)) !important; }

/* ---------- 3. Cards and tiles ---------- */
/* Pointer spotlight: a soft light that follows the cursor across a card (vars set by ptr-motion.js, which only
   runs when motion is allowed). */
.pm-spot { position: relative; }
.pm-spot > .pm-light {
  position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 3; opacity: 0;
  background: radial-gradient(380px circle at var(--pm-x, 50%) var(--pm-y, 50%), rgba(255, 255, 255, .22), rgba(128, 237, 239, .08) 40%, transparent 65%);
  mix-blend-mode: soft-light;
}

/* ---------- 4. Links in copy: the text's own colour and an underline at rest, colour and glow only on hover ---------- */
/* EVERY PAGE, EVERY INLINE LINK. Operator 2026-09-27, round 4, the third time: "you were not suppose to make hyperlink
   text blue when not clicked it looks tacky; its suppose to be visual it is hyperlink with underline not all text then
   changes color when hovered or clicked"; "the text color is only suppose to change when hoovered and clicked but its
   still blue". So a copy link AT REST reads in its paragraph's own colour (white in a dark band, ink on a light one)
   and is marked by a thin underline only: 1px, 3px below the baseline, in the text's own colour at 55 percent. Its
   colour changes ONLY on :hover, :focus-visible and :active (a tap on a phone): the words take the header's gradient,
   the rest colour of the band (--pm-link: brand blue on light bands, cyan on dark ones) to violet, with a soft glow,
   and the underline turns violet. The computed colour moves to violet so a hover is measurable
   (tools/check-link-glow.mjs rule (a) now FAILS a link whose colour at rest differs from its parent's, and one with
   no underline). Excluded, because they have their own treatment: buttons and pills, the ring controls, tile and
   card links, the header, the hamburger, the footer group, Talk to Us, the enquiry form and the hero. */
:root { --pm-link: #2B5FD9; --pm-link-hover: #A567F1; --pm-link-glow: rgba(109, 87, 255, .45); }
.ptrsx-dark, .ptrsx-ctaband, .twinPhotoCopy, .homeTwinWays, .homeFormatDidymo, .ptrin-dark, [data-screen-label="Featured work with"] { --pm-link: #80EDEF; --pm-link-hover: #A567F1; --pm-link-glow: rgba(128, 237, 239, .55); }
:is(.ptrdr, .homeFormatBand, .twinPhotoCopy, .homeWork, .homeBuildLead, [data-screen-label]) :is(p, li, td, th, figcaption, dd, dt, blockquote, h2, h3, h4, .ptrsx-lead, .ptrsx-bs, .ptrsx-measure, .ptrsx-srccopy, .ptrsx-attr) a:not(.pm-btn):not(.ptrsx-pillbtn):not(.pm-ring):not(.twinPhotoLink):not(.ptrsx-tilelink):not(.ptrin-btn):not(.homeFormatTile):not(.fwCard):not(.homeBuildType):not(.stChip):not(.ptrxr-card):not(.ptrsx-dlstretch):not(:is([class*="ptrNav"], [class*="ptrMenu"], [class*="ptrFooter"], [class*="fgLinks"], [class*="fgConnect"], [class*="ttu"], [class*="ptrEnquiry"], [class*="ldb"], [class*="asd"], [class*="wcs"], [class*="mq-"], #cinWrap, .homeNewsCard) a),
.ptrsx-measure a:not(.pm-btn):not(.ptrsx-pillbtn),
.ptrsx-lead a:not(.pm-btn):not(.ptrsx-pillbtn),
.ptrsx-bs a,
.ptrsx-srccopy h3 a,
.ptrsx-pcopy p a:not(.pm-btn):not(.ptrsx-pillbtn):not(.twinPhotoLink),
.ptrsx-scopy a:not(.pm-btn):not(.ptrsx-pillbtn),
.ptrsx-spill a:not(.twinPhotoLink):not(.pm-btn):not(.ptrsx-pillbtn),
.twinPhotoCopy p > a:not(.twinPhotoLink),
.homeFormatCopy p a,
.homeFormatLead a,
.homeFormatLead .homeLeadTwin,
.homeFormatBand p > a:not(.pm-btn),
.homeFormatBand h2 > a,
.homeNewsCard > p + a,
.homeLink,
.ptrdr table.ptrin-ev a,
.ptrdr .ptrin-read a,
.ptrdr ol.ptrin-dec a,
.ptrdr .ptrin-limits a,
.ptrdr .ptrin-cite__data a,
.ptrdr .ptrch-src a {
  color: inherit !important; -webkit-text-fill-color: currentColor; background-image: none !important; text-shadow: none;
  text-decoration-line: underline !important; text-decoration-style: solid !important; text-decoration-thickness: 1px !important;
  text-underline-offset: 3px !important; text-decoration-color: color-mix(in srgb, currentColor 55%, transparent) !important;
}
@media (hover: hover) {
  :is(.ptrdr, .homeFormatBand, .twinPhotoCopy, .homeWork, .homeBuildLead, [data-screen-label]) :is(p, li, td, th, figcaption, dd, dt, blockquote, h2, h3, h4, .ptrsx-lead, .ptrsx-bs, .ptrsx-measure, .ptrsx-srccopy, .ptrsx-attr) a:not(.pm-btn):not(.ptrsx-pillbtn):not(.pm-ring):not(.twinPhotoLink):not(.ptrsx-tilelink):not(.ptrin-btn):not(.homeFormatTile):not(.fwCard):not(.homeBuildType):not(.stChip):not(.ptrxr-card):not(.ptrsx-dlstretch):not(:is([class*="ptrNav"], [class*="ptrMenu"], [class*="ptrFooter"], [class*="fgLinks"], [class*="fgConnect"], [class*="ttu"], [class*="ptrEnquiry"], [class*="ldb"], [class*="asd"], [class*="wcs"], [class*="mq-"], #cinWrap, .homeNewsCard) a):hover,
.ptrsx-measure a:not(.pm-btn):not(.ptrsx-pillbtn):hover,
.ptrsx-lead a:not(.pm-btn):not(.ptrsx-pillbtn):hover,
.ptrsx-bs a:hover,
.ptrsx-srccopy h3 a:hover,
.ptrsx-pcopy p a:not(.pm-btn):not(.ptrsx-pillbtn):not(.twinPhotoLink):hover,
.ptrsx-scopy a:not(.pm-btn):not(.ptrsx-pillbtn):hover,
.ptrsx-spill a:not(.twinPhotoLink):not(.pm-btn):not(.ptrsx-pillbtn):hover,
.twinPhotoCopy p > a:not(.twinPhotoLink):hover,
.homeFormatCopy p a:hover,
.homeFormatLead a:hover,
.homeFormatLead .homeLeadTwin:hover,
.homeFormatBand p > a:not(.pm-btn):hover,
.homeFormatBand h2 > a:hover,
.homeNewsCard > p + a:hover,
.homeLink:hover,
.ptrdr table.ptrin-ev a:hover,
.ptrdr .ptrin-read a:hover,
.ptrdr ol.ptrin-dec a:hover,
.ptrdr .ptrin-limits a:hover,
.ptrdr .ptrin-cite__data a:hover,
.ptrdr .ptrch-src a:hover {
    opacity: 1; color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1)) !important;
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
    text-decoration-color: var(--pm-link-hover, #A567F1) !important; filter: drop-shadow(0 0 9px var(--pm-link-glow, rgba(109, 87, 255, .45)));
  }
}
:is(.ptrdr, .homeFormatBand, .twinPhotoCopy, .homeWork, .homeBuildLead, [data-screen-label]) :is(p, li, td, th, figcaption, dd, dt, blockquote, h2, h3, h4, .ptrsx-lead, .ptrsx-bs, .ptrsx-measure, .ptrsx-srccopy, .ptrsx-attr) a:not(.pm-btn):not(.ptrsx-pillbtn):not(.pm-ring):not(.twinPhotoLink):not(.ptrsx-tilelink):not(.ptrin-btn):not(.homeFormatTile):not(.fwCard):not(.homeBuildType):not(.stChip):not(.ptrxr-card):not(.ptrsx-dlstretch):not(:is([class*="ptrNav"], [class*="ptrMenu"], [class*="ptrFooter"], [class*="fgLinks"], [class*="fgConnect"], [class*="ttu"], [class*="ptrEnquiry"], [class*="ldb"], [class*="asd"], [class*="wcs"], [class*="mq-"], #cinWrap, .homeNewsCard) a):is(:focus-visible, :active),
.ptrsx-measure a:not(.pm-btn):not(.ptrsx-pillbtn):is(:focus-visible, :active),
.ptrsx-lead a:not(.pm-btn):not(.ptrsx-pillbtn):is(:focus-visible, :active),
.ptrsx-bs a:is(:focus-visible, :active),
.ptrsx-srccopy h3 a:is(:focus-visible, :active),
.ptrsx-pcopy p a:not(.pm-btn):not(.ptrsx-pillbtn):not(.twinPhotoLink):is(:focus-visible, :active),
.ptrsx-scopy a:not(.pm-btn):not(.ptrsx-pillbtn):is(:focus-visible, :active),
.ptrsx-spill a:not(.twinPhotoLink):not(.pm-btn):not(.ptrsx-pillbtn):is(:focus-visible, :active),
.twinPhotoCopy p > a:not(.twinPhotoLink):is(:focus-visible, :active),
.homeFormatCopy p a:is(:focus-visible, :active),
.homeFormatLead a:is(:focus-visible, :active),
.homeFormatLead .homeLeadTwin:is(:focus-visible, :active),
.homeFormatBand p > a:not(.pm-btn):is(:focus-visible, :active),
.homeFormatBand h2 > a:is(:focus-visible, :active),
.homeNewsCard > p + a:is(:focus-visible, :active),
.homeLink:is(:focus-visible, :active),
.ptrdr table.ptrin-ev a:is(:focus-visible, :active),
.ptrdr .ptrin-read a:is(:focus-visible, :active),
.ptrdr ol.ptrin-dec a:is(:focus-visible, :active),
.ptrdr .ptrin-limits a:is(:focus-visible, :active),
.ptrdr .ptrin-cite__data a:is(:focus-visible, :active),
.ptrdr .ptrch-src a:is(:focus-visible, :active) {
    opacity: 1; color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1)) !important;
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
    text-decoration-color: var(--pm-link-hover, #A567F1) !important; filter: drop-shadow(0 0 9px var(--pm-link-glow, rgba(109, 87, 255, .45)));
}
@media (prefers-reduced-motion: no-preference) {
  :is(.ptrdr, .homeFormatBand, .twinPhotoCopy, .homeWork, .homeBuildLead, [data-screen-label]) :is(p, li, td, th, figcaption, dd, dt, blockquote, h2, h3, h4, .ptrsx-lead, .ptrsx-bs, .ptrsx-measure, .ptrsx-srccopy, .ptrsx-attr) a:not(.pm-btn):not(.ptrsx-pillbtn):not(.pm-ring):not(.twinPhotoLink):not(.ptrsx-tilelink):not(.ptrin-btn):not(.homeFormatTile):not(.fwCard):not(.homeBuildType):not(.stChip):not(.ptrxr-card):not(.ptrsx-dlstretch):not(:is([class*="ptrNav"], [class*="ptrMenu"], [class*="ptrFooter"], [class*="fgLinks"], [class*="fgConnect"], [class*="ttu"], [class*="ptrEnquiry"], [class*="ldb"], [class*="asd"], [class*="wcs"], [class*="mq-"], #cinWrap, .homeNewsCard) a) { transition: color var(--pm-micro) var(--pm-ease), filter var(--pm-micro) var(--pm-ease), text-decoration-color var(--pm-micro) var(--pm-ease); }
}

/* CARD TEXT AT REST IS THE CARD'S OWN COLOUR (round 4: "NO link or text is blue at rest"). A card link's title, its
   arrow and a download card's stretched title read in the card's text colour at rest; the card-title rule below
   colours them on hover only, and the ring (section 3) marks the card. */
.homeNewsCard > a, .homeNewsCard > a h3, .ptrsx-dlcard .ptrsx-dltitle a.ptrsx-dlstretch, .homeArrow { color: inherit !important; }
.ptrsx-dlcard .ptrsx-dltitle a.ptrsx-dlstretch { position: static; text-decoration: none !important; background-image: none !important; }
.ptrsx-dlcard .ptrsx-dltitle a.ptrsx-dlstretch::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.ptrsx-dlcard .ptrsx-dlcover, .ptrsx-dlcard .ptrsx-spill { position: relative; z-index: 2; }
/* The headline link itself takes the hover colour (its h3 wears the gradient from the card-title rule below), so the
   link measures as hovered (check-link-glow reads the <a>). */
@media (hover: hover) { .homeNewsCard > a:hover { color: var(--pm-link-hover, #A567F1) !important; } }

/* ---------- 5. Everything that MOVES: only when the visitor has not asked for reduced motion ---------- */
@media (prefers-reduced-motion: no-preference) {
  .ptrsx-pillbtn, .pm-btn, .pm-ring, .twinPhotoLink, .twinPhotoActions button {
    transition: transform var(--pm-micro) var(--pm-ease), box-shadow var(--pm-micro) var(--pm-ease), background-color var(--pm-micro) var(--pm-ease),
                border-color var(--pm-micro) var(--pm-ease), color var(--pm-micro) var(--pm-ease);
  }
  .ptrsx-pillbtn::after, .pm-btn::after, .pm-ring::after, .twinPhotoLink::after, .twinPhotoActions button::after { transition: opacity var(--pm-micro) var(--pm-ease); }
  .ptrsx-pillbtn:active, .pm-btn:active, .twinPhotoLink:active, .twinPhotoActions button:active { transform: translateY(0) scale(.965); transition-duration: 120ms; }
  .ptrsx-pillbtn:focus-visible::after, .pm-btn:focus-visible::after, .pm-ring:focus-visible::after, .twinPhotoLink:focus-visible::after,
  .pm-ring-host:focus-visible .pm-ring::after { animation: pm-spin 2.6s linear infinite; }
  .ptrsx-tilelink::after, .fwCard::after, .homeNewsCard::after, .homeFormatTile::after, .ptrsx-dlcard::after { transition: opacity var(--pm-micro) var(--pm-ease); }
  /* The featured-work cards sit under Home's hero and beside six autoplaying loops, where a frame can take 200 ms;
     a 180 ms fade there was measured still short of opaque 400 ms after the pointer arrived (check-link-glow), so the
     ring on those cards arrives at once and only the halo animates. */
  .fwCard::after { transition: none; }
  .ptrsx-tilelink:focus-visible::after, .fwCard:focus-visible::after, .homeNewsCard:focus-visible::after, .homeFormatTile:focus-visible::after { animation: pm-spin 2.6s linear infinite; }
  .pm-field:hover:not(:focus) { animation: pm-field-glow 3.2s linear infinite; }
  .ptrsx-tilelink, .fwCard, .homeNewsCard, .homeFormatTile, .ptrsx-srcrow, .ptrsx-dlcard, .ptrcx-grid-3 > figure {
    transition: transform var(--pm-micro) var(--pm-ease), box-shadow var(--pm-micro) var(--pm-ease);
  }
  .pm-spot > .pm-light { transition: opacity var(--pm-micro) var(--pm-ease); }
  .ptrsx-measure a, .ptrsx-lead a, .ptrsx-bs a, .ptrsx-srccopy h3 a, .ptrsx-pcopy p a, .ptrsx-scopy a, .ptrsx-spill a:not(.twinPhotoLink),
  .twinPhotoCopy p > a:not(.twinPhotoLink), .homeFormatCopy p a, .homeFormatLead a, .homeFormatLead .homeLeadTwin, .homeFormatBand p > a:not(.pm-btn),
  .homeFormatBand h2 > a, .homeNewsCard > p + a, .homeLink {
    transition: color var(--pm-micro) var(--pm-ease), filter var(--pm-micro) var(--pm-ease);
  }
  @media (hover: hover) {
    .ptrsx-pillbtn:hover, .pm-btn:hover, .twinPhotoLink:hover, .twinPhotoActions button:hover { transform: translateY(-2px); animation: pm-halo 3.2s linear infinite; }
    .pm-ring:hover, .pm-ring-host:hover .pm-ring { animation: pm-halo 3.2s linear infinite; }
    .pm-ring-host:hover .pm-ring::after { animation: pm-spin 2.6s linear infinite; }
    .ptrsx-pillbtn:hover::after, .pm-btn:hover::after, .pm-ring:hover::after, .twinPhotoLink:hover::after, .twinPhotoActions button:hover::after { animation: pm-spin 2.6s linear infinite; }
    .ptrsx-tilelink:hover::after, .fwCard:hover::after, .homeNewsCard:hover::after, .homeFormatTile:hover::after, .ptrsx-dlcard:hover::after { animation: pm-spin 2.6s linear infinite; }
    .ptrsx-tilelink:hover, .fwCard:hover, .homeNewsCard:hover, .ptrsx-dlcard:hover {
      transform: translateY(-5px); box-shadow: 0 22px 48px rgba(11, 13, 18, .14);
    }
    .ptrcx-grid-3 > figure:hover, .homeFormatTile:hover, .ptrsx-srcrow:hover { transform: translateY(-3px); box-shadow: 0 16px 36px rgba(11, 13, 18, .10); }
    .pm-spot:hover > .pm-light { opacity: 1; }
    .ptrsx-pmedia video, .ptrsx-pmedia img { transition: transform var(--pm-micro) var(--pm-ease), filter var(--pm-micro) var(--pm-ease); }
    .ptrsx-pair:hover .ptrsx-pmedia video, .ptrsx-pair:hover .ptrsx-pmedia img { transform: scale(1.02); filter: saturate(1.08); }
  }
}

/* ---------- 4b. Headings wrap in balance (audit fix 2026-09-27, P4 and P10) ---------- */
/* An 84px Home heading left "practice." alone on its second line, and the footer group's "Try what we're /
   building." does the same on every page. Every h1 and h2 the site draws balances its lines (Chromium, Safari and
   Firefox all honour it; a browser that does not simply wraps as before). Element-level, so any heading rule that
   asks for another wrap still wins. */
h1, h2 { text-wrap: balance; }

/* ---------- 5b. Rails scroll, never show a scrollbar (operator 2026-09-27) ---------- */
/* A rail may scroll by swipe, wheel or keyboard but never shows a bar (Windows Chrome draws classic bars on
   desktop). Stylesheet and page-block rules carry their own scrollbar-width:none and ::-webkit-scrollbar twin;
   these catch the rails whose overflow is set INLINE (chart tables, diagrams), which a class rule cannot reach.
   tools/check-no-horizontal-scrollbars.mjs holds every route to it at 1440, 1024 and 390. */
[style*="overflow-x:auto"], [style*="overflow-x: auto"], [style*="overflow-x:scroll"], [style*="overflow-x: scroll"] { scrollbar-width: none; }
[style*="overflow-x:auto"]::-webkit-scrollbar, [style*="overflow-x: auto"]::-webkit-scrollbar,
[style*="overflow-x:scroll"]::-webkit-scrollbar, [style*="overflow-x: scroll"]::-webkit-scrollbar { display: none; }

/* ---------- 6. Reduced motion: belt and braces for anything a page styles itself ---------- */
/* Every animation above is off; a hover still changes colour and shows the ring, instantly. */
@media (prefers-reduced-motion: reduce) {
  #pm-progress { display: none; }
  .ptrsx-pillbtn, .pm-btn, .pm-ring, .twinPhotoLink, .twinPhotoActions button,
  .ptrsx-pillbtn::after, .pm-btn::after, .pm-ring::after, .twinPhotoLink::after, .twinPhotoActions button::after { animation: none !important; }
}

/* ==================================================================================================================
 * SITEWIDE POLISH S22 + S23 (2026-09-27, sitewide lane). Two card families that did not answer the pointer.
 * S22, the case-study cards (.vcCard on /work/case-studies): operator "these cards have no hover effect". The whole
 *   card is the click target: its one "Read the case study ->" link is stretched over the card (::before, so the
 *   link's own hover colour answers anywhere on the card), the shared ring sits 3px outside at the card's radius + 3
 *   (the clip moves from the card to its top picture), and the title takes the card-title gradient on hover.
 * S23, the /ai/didymo "Ways in" cards (a.ptrsx-chan.ptrsx-tilelink): operator "these are not clickable pages cards".
 *   Each card is a link to its page, carries the twin dock's icon above its title, keeps the band's text colour, and
 *   ends in an "Explore ... ->" line underlined at rest like a copy link; the ring sits 10px out (the words run to
 *   the card's edge, as on Home's cards).
 * ================================================================================================================== */
.vcCard { position: relative; --pm-r: 18px; }
html body .vcCard.vcCard { overflow: visible; }
.vcCard > :first-child:is(img, video, picture, figure) { border-radius: 17px 17px 0 0; overflow: hidden; }
.vcCard p:last-child > a:only-child::before { content: ''; position: absolute; inset: 0; z-index: 3; border-radius: inherit; }
.vcCard::after {
  content: ''; position: absolute; inset: calc(-1 * var(--pm-ring-out, 3px)); padding: 2px; border-radius: calc(var(--card-r, var(--pm-r, 18px)) + var(--pm-ring-out, 3px)); pointer-events: none; z-index: 4;
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.vcCard:focus-within::after { opacity: 1; }
.ptrdr a.ptrsx-chan { color: inherit; text-decoration: none; --pm-r: 16px; --pm-ring-out: 10px; }
.ptrdr .ptrsx-chan .ptrsx-chanicon { display: inline-flex; width: 28px; height: 28px; color: currentColor; }
.ptrdr .ptrsx-chan .ptrsx-chanicon svg { width: 28px; height: 28px; display: block; }
.ptrdr .ptrsx-chan p.ptrsx-chanmore { margin-top: auto; font-weight: 600; text-decoration: underline 1px color-mix(in srgb, currentColor 55%, transparent); text-underline-offset: 3px; }
@media (hover: hover) {
  .vcCard:hover::after { opacity: 1; }
  :is(.vcCard, a.ptrsx-chan):hover :is(h3, .ptrsx-chanmore) {
    color: var(--pm-link-hover, #A567F1); background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
  a.ptrsx-chan:hover .ptrsx-chanmore { text-decoration-color: var(--pm-link-hover, #A567F1); }
}
@media (prefers-reduced-motion: no-preference) {
  .vcCard::after { transition: opacity var(--pm-micro) var(--pm-ease); }
  @media (hover: hover) { .vcCard:hover::after { animation: pm-spin 2.6s linear infinite; } }
}
/* S22 follow-up: a card that is not itself a link (.pm-card) is clickable as a whole through its one "Read/Explore ->"
 * link (a.pm-stretch, stretched over the card); any other link in it sits above the stretch. The shared ring sits 3px
 * outside at the card's radius + 3 (the tile's clip moves to its picture), the title takes the gradient on hover. */
.pm-card { position: relative; --pm-r: 20px; }
.ptrdr .ptrsx-tile.pm-card { overflow: visible; }
.ptrdr .ptrsx-tile.pm-card > :first-child:is(img, video, picture, figure) { border-radius: 19px 19px 0 0; overflow: hidden; }
.pm-card a.pm-stretch::before { content: ''; position: absolute; inset: 0; z-index: 3; border-radius: inherit; }
.pm-card a[href]:not(.pm-stretch) { position: relative; z-index: 5; }
.pm-card::after {
  content: ''; position: absolute; inset: calc(-1 * var(--pm-ring-out, 3px)); padding: 2px; border-radius: calc(var(--card-r, var(--pm-r, 20px)) + var(--pm-ring-out, 3px)); pointer-events: none; z-index: 4;
  background: conic-gradient(from var(--pm-angle), #4285F4, #9B72CB, #D96570, #F4B400, #34A853, #41BBF5, #4285F4); opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.pm-card:focus-within::after { opacity: 1; }
@media (hover: hover) {
  .pm-card:hover::after { opacity: 1; }
  .pm-card:hover :is(figcaption strong, h3) {
    color: var(--pm-link-hover, #A567F1); background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .pm-card::after { transition: opacity var(--pm-micro) var(--pm-ease); }
  @media (hover: hover) { .pm-card:hover::after { animation: pm-spin 2.6s linear infinite; } }
}
/* A stretched link must not take the copy link's hover glow as a FILTER: a filter makes the link the containing block of
 * its own ::before, so on hover the stretch shrank to the words and a click on the card's title or picture landed on
 * nothing (measured: mousedown on the link, mouseup on .vcCardBody, no navigation). The glow becomes a text-shadow. */
.vcCard p:last-child > a:only-child, .pm-card a.pm-stretch { filter: none !important; }
@media (hover: hover) {
  .vcCard p:last-child > a:only-child:hover, .pm-card a.pm-stretch:hover { text-shadow: 0 0 9px var(--pm-link-glow, rgba(109, 87, 255, .45)); }
}

/* ==================================================================================================================
 * EVERY CARD TAKES HOME'S ROUND 4 TREATMENT (operator 2026-09-28, R1 R5 R6: "the glow stroke on the cards is covering
 * text"; the bold titles on the /training buyer and planning cards and the /xr "How PTR builds" cards "are supposed to
 * change color on hover and click"). Round 4 fixed Home's cards only: the ring sat 10px out there, the title rule
 * matched Home's caption shapes (figcaption strong, h3, build names). Elsewhere a card kept the 3px ring, a card
 * whose overflow is clipped INLINE (the /xr hub and case cards: style="...overflow:hidden") clipped its own ring to a
 * sliver, and a title written as <p><strong> (the /training cards) or as an inline-styled bold <div> (the /xr cards)
 * matched no title selector, so it never changed. Now:
 *   1. every clickable card draws the ring 10px out at its radius + 10 (--pm-ring-out), as on Home;
 *   2. a card that clips inline stops clipping; its top picture rounds its own corners instead;
 *   3. a card's bold title and its "Explore ... ->" / "Read ... ->" line take the card-title gradient on hover AND on
 *      a click or keyboard focus (:active, :focus-visible), whatever element the page wrote them in.
 * ================================================================================================================== */
.ptrsx-tilelink, .vcCard, .pm-card { --pm-ring-out: 10px; }
html body a.ptrsx-tilelink:is([style*="overflow:hidden"], [style*="overflow: hidden"]) { overflow: visible !important; }
html body a.ptrsx-tilelink:is([style*="overflow:hidden"], [style*="overflow: hidden"]) > :first-child:is(img, video, picture, figure) {
  border-radius: var(--card-r, var(--pm-r, 20px)) var(--card-r, var(--pm-r, 20px)) 0 0; overflow: hidden;
}
@media (hover: hover) {
  .homeFormatTile:hover .homeWorkStory > strong,
  .ptrsx-tilelink:hover :is(p > strong:first-child, figcaption > strong, figcaption > b, .xrev-title, .xr-explore, .ptrsx-tlab,
    [style*="font-weight:700"], [style*="font-weight: 700"], [style*="font-weight:600"], [style*="font-weight: 600"]) {
    color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
}
.ptrsx-tilelink:is(:active, :focus-visible) :is(p > strong:first-child, figcaption > strong, figcaption > b, .xrev-title, .xr-explore, .ptrsx-tlab,
  [style*="font-weight:700"], [style*="font-weight: 700"], [style*="font-weight:600"], [style*="font-weight: 600"]),
:is(.homeFormatTile, .homeNewsCard, .fwCard, .ptrsx-dlcard, .homeBuildType):is(:active, :focus-visible) :is(figcaption strong, .homeFourStory strong, h3, .fwMore, .homeBuildName, .ptrsx-dltitle),
.homeFormatTile:is(:active, :focus-visible) > figure:last-child figcaption,
.homeFormatTile:is(:active, :focus-visible) .homeWorkStory > strong,
:is(.vcCard, .pm-card, a.ptrsx-chan):is(:active, :focus-visible, :has(:focus-visible)) :is(figcaption strong, h3, .ptrsx-chanmore) {
  color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1));
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.ptrsx-tilelink:active::after, .fwCard:active::after, .homeNewsCard:active::after, .homeFormatTile:active::after, .ptrsx-dlcard:active::after { opacity: 1; }
/* The gradient spans its element's box: a bold title <div> is as wide as the card, so a short title ("Google") read
   in the blue end only. Those divs are as wide as their words (left aligned, so nothing moves). */
.ptrsx-tilelink div:is([style*="font-weight:700"], [style*="font-weight: 700"], [style*="font-weight:600"], [style*="font-weight: 600"]) { width: fit-content; max-width: 100%; }
/* The sweep that followed (check-link-glow rule (f), 180 routes on the channel, 2026-09-28) found two more card families
   whose bold title stayed still on hover: the source cards (.ptrsx-srcrow, 69 cards: the title is a link that coloured
   only when the pointer was on the words) and the insights key figures (.ptrin-key, 28 tiles: the number). They take
   the same gradient when the card is hovered, pressed or holds keyboard focus. The source cards are dark, so their
   gradient runs cyan to violet (--pm-link is cyan there). */
@media (hover: hover) {
  .ptrsx-srcrow:hover .ptrsx-srccopy :is(h2, h3), .ptrsx-srcrow:hover .ptrsx-srccopy :is(h2, h3) a,
  .ptrin-key:hover .ptrin-key__num {
    color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1)) !important;
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  }
}
.ptrsx-srcrow:is(:active, :has(:focus-visible)) .ptrsx-srccopy :is(h2, h3), .ptrsx-srcrow:is(:active, :has(:focus-visible)) .ptrsx-srccopy :is(h2, h3) a,
.ptrin-key:is(:active, :focus-visible) .ptrin-key__num {
  color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1)) !important;
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
/* R2/R3 hunt (2026-09-28, 32 routes x 1440 + 393, every copy link and twin-band control at rest vs hover, keyboard focus
   and press): two families did not answer. The twin dock buttons (.twinPhotoActions, 12 on 3 pages) lit their ring on
   hover only, never on keyboard focus; a download card's stretched title (.ptrsx-dlstretch, 10) changed on hover only,
   never on focus or press. Both now answer every state the copy links do. */
.twinPhotoActions button:focus-visible::after, .ptrsx-dlcard:has(.ptrsx-dlstretch:is(:focus-visible, :active))::after { opacity: 1; }
.ptrsx-dlcard .ptrsx-dltitle a.ptrsx-dlstretch:is(:focus-visible, :active) {
  color: var(--pm-link-hover, #A567F1) !important; background-image: linear-gradient(90deg, var(--pm-link, #2B5FD9), var(--pm-link-hover, #A567F1)) !important;
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
