/* Fix layer. Loaded after the Webflow stylesheet so these rules win.
   Edit this file, then run: node build.mjs
   Colours here go through the same content.json colors remap as everything else. */

/* ---------------------------------------------------------------------------
   1. Hero content getting covered by mobile browser chrome.

   .hero is height:100vh and its content bottom-aligns (margin-top:auto on
   .heading_hero, justify-content:flex-end on .flexbox_hero), so the "Get in
   Touch" button and the SCROLL DOWN caption sit hard against the bottom edge.

   On a phone, 100vh resolves to the viewport with the browser UI HIDDEN, the
   largest it ever gets. At rest the URL bar is showing, so the bottom ~90px of
   the hero is underneath it, and that is exactly where the button and caption
   live.

   100svh is the SMALL viewport height: the size with browser chrome visible.
   It is the conservative one and, unlike 100dvh, it does not change while you
   scroll, so the hero never resizes mid-scroll. The site's own stylesheet
   already switches .wrapper_hero to 100svh below 480px, so this just applies
   the same intent at every width and to .hero itself.

   Desktop is unaffected: with no chrome overlaying content, svh == vh.
   --------------------------------------------------------------------------- */
.hero,
.hero_career,
.hero_section {
  height: 100vh;      /* fallback for browsers without svh */
  height: 100svh;
}

/* ---------------------------------------------------------------------------
   2. Keep the bottom padding the design already specifies, and add the
      home-indicator inset on top of it.

   These restate Webflow's own per-breakpoint padding-bottom. They must stay in
   sync with the base stylesheet:
     base   2.5em      (max-width:991px) 4em
     (max-width:767px) 4em        (max-width:479px) 10em
   A single blanket value here would flatten those and pull the button back to
   the edge on phones.
   --------------------------------------------------------------------------- */
.wrapper_hero {
  padding-bottom: calc(2.5em + env(safe-area-inset-bottom, 0px));
}
@media screen and (max-width: 991px) {
  .wrapper_hero { padding-bottom: calc(4em + env(safe-area-inset-bottom, 0px)); }
}
@media screen and (max-width: 767px) {
  .wrapper_hero { padding-bottom: calc(4em + env(safe-area-inset-bottom, 0px)); }
}
@media screen and (max-width: 479px) {
  .wrapper_hero { padding-bottom: calc(10em + env(safe-area-inset-bottom, 0px)); }
}

/* ---------------------------------------------------------------------------
   3. Product Suite section: silver instead of the original near-black #151515.

   A flat hex can only ever read as grey. Metal reads as metal because of the
   specular highlight, so this is a multi-stop gradient: bright bands where the
   light catches, cooler troughs between them, with a slight blue cast in the
   shadows the way real polished silver behaves.

   The sheen runs ACROSS the width (90deg) rather than down. The section is
   ~4730px tall, so a vertical or diagonal gradient would smear over that
   distance or need tiling that shows visible seams. A horizontal sheen tiles
   perfectly at any height and stays consistent as the cards scroll past.
   --------------------------------------------------------------------------- */
:root {
  /* flat mid-tone of the gradient below; the pixel transitions dissolve to this */
  --silver-base: #b9c0c7;
  --silver-ink: #1b2026;
}

.products {
  background-color: var(--silver-base);
  /* The troughs bottom out at #868e96 rather than going darker. Text crosses this
     gradient at arbitrary x depending on viewport width, so every stop has to stay
     light enough for --silver-ink to clear 4.5:1. #868e96 gives 4.88:1; the #727a83
     it replaced gave only 3.77:1 and the right-hand paragraph sat directly on it.
     Highlights stay at pure white, so the sheen still has full range. */
  background-image: linear-gradient(
    90deg,
    #8a929a 0%,
    #bcc3ca 7%,
    #ffffff 14%,
    #c8ced4 21%,
    #868e96 33%,
    #e9edf0 46%,
    #ffffff 53%,
    #aeb6bd 66%,
    #888f97 80%,
    #d2d8dd 92%,
    #949ca4 100%
  );
}

/* Text that sat white on the old near-black now needs to be dark on silver.
   Scoped deliberately: the product cards keep their own pink background and
   their white text, so they are excluded rather than swept up by inheritance. */
.products .products_heading,
.products .h2.smallest,
.products .flex_products .p_gen,
.products .content_bottom .p_gen {
  color: var(--silver-ink);
}

/* ---------------------------------------------------------------------------
   4. No scrolling above the top of the page.

   overscroll-behavior-y: none removes the rubber-band bounce past the top edge,
   so the hero cannot be dragged down to reveal empty space above it.
   --------------------------------------------------------------------------- */
html,
body {
  overscroll-behavior-y: none;
}

/* ---------------------------------------------------------------------------
   5. Header logo on light sections.

   The site ships a script that is meant to swap .svg_logo to a second, darker
   logo whenever the header sits over a [data-section="light"] block. It sets
   .is-light-section correctly but the swap itself never lands: verified on
   www.cantor8.io/newsroom, where the class is on and the logo stays the white
   file. Their own logo is white, so on every light page the mark is invisible.

   We inherited that faithfully. Rather than patch their script, drive the swap
   from the class that does work. !important is required because their script
   writes the white logo to the element's inline style.
   --------------------------------------------------------------------------- */
.header.is-light-section .svg_logo {
  background-image: url("../../images/brand-logo-pink.png") !important;
  opacity: 1 !important;
}

/* ---------------------------------------------------------------------------
   6. The 2026-07-28 content pass.

   Two blocks changed shape, not just wording, so the Webflow layout that held
   the English no longer fits:

     a. the 我們的團隊 paragraph became a single large line,
     b. the partner logo wall became 環境介紹, three photographs.

   The FOUNDATION little article deliberately has NO rules here. An earlier pass
   wrapped its text in <p> elements and widened the column to fit a four part
   statement. Both are reverted: he asked for the block to line up exactly like
   Cantor8's, and the short copy fits the original 42ch column. The <p> wrappers
   also carried a real bug worth remembering. Webflow's stylesheet ships a bare

       p { color: var(--black) }

   and a direct rule on p beats the white colour inherited from .p_gen, so the
   moment his text sat inside <p> it rendered #151515 on the pink. Text that
   lives bare inside .p_gen, the way Cantor8 ships it, inherits white correctly.
   Do not reintroduce <p> in these blocks without setting colour explicitly.
   --------------------------------------------------------------------------- */

/* a. 我們的團隊 motto ------------------------------------------------------ */
/* Sits at the foot of the silver Product Suite section, so it takes the dark
   ink, not the white used everywhere else on the pink. */
.team_motto {
  color: var(--silver-ink);
  font-size: clamp(1.6rem, 3.2vw, 2.75rem);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.02em;
}

/* b. 環境介紹 -------------------------------------------------------------- */
/* No lede copy was supplied. The element stays in the DOM so one can be dropped
   in later from content.json, but collapses while it is empty. */
.env_lede:empty {
  display: none;
}
/* Their grid is six columns holding eight small centred logos, deliberately
   scattered. Three photographs want three wide cells in one row.

   The scatter is not in the stylesheet. It comes from a <style> embed in the
   body that sets .grid_partners.w-dyn-items to six columns and then pins each
   tile with .partner_item:nth-child(8n+1..8) rules, and a script that copies
   the same placement onto each item as an inline grid-area. So:

     - the column count needs .w-dyn-items in the selector, because a body
       <style> beats a linked stylesheet at equal specificity, and
     - the per-item placement needs !important, because an inline style beats
       any stylesheet without it.

   grid-area:auto resets all four placement longhands in one declaration. */
.cms_grid .grid_partners.w-dyn-items {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: auto;
  grid-column-gap: 1.5em;
  grid-row-gap: 2.5em;
  gap: 1.5em 1.5em;
}
.cms_grid .grid_partners .partner_item {
  grid-area: auto !important;
}
.cms_grid .partner_item {
  display: flex;
  flex-direction: column;
}
.env_media {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.08);
}
/* .partner_box is a centred flex box with padding, built to letterbox a logo.
   A photograph fills its cell instead. */
.cms_grid .partner_box.env_box {
  padding: 0;
  display: block;
}
.cms_grid .env_box img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.env_caption {
  margin-top: 0.9em;
  text-align: center;
  font-size: 0.9em;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.86);
}

@media (max-width: 991px) {
  .cms_grid .grid_partners.w-dyn-items {
    grid-column-gap: 1em;
    grid-row-gap: 1.75em;
    gap: 1.75em 1em;
  }
}
@media (max-width: 767px) {
  .cms_grid .grid_partners.w-dyn-items {
    grid-template-columns: 1fr;
  }
  .env_media {
    aspect-ratio: 16 / 10;
  }
}

/* ---------------------------------------------------------------------------
   7. Hero mascot.

   A character that sits IN FRONT of the hero canvas, not instead of it. The
   canvas is the animated centrepiece; replacing it would make the hero quieter.

   The artwork ships as three files so the eyes can move independently:
     images/mascot-body.png    the full 636x799 frame with both eye ovals cut
                               out and the face inpainted behind them
     images/mascot-eye-l.png   101x111 sprite, rest position 77,392 in that frame
     images/mascot-eye-r.png   101x111 sprite, rest position 289,392 (exactly
                               +212px in x, they are the same drawing twice)

   Every percentage below is that pixel geometry divided by 636x799, so the eyes
   stay registered to the face at any rendered width without a second set of
   numbers per breakpoint. Do not round them: the eye sprite is 101px of a 636px
   frame, and a 0.1% error is visible as a seam against the inpainted socket.

   LAYERING. main.hero is position:relative with z-index:auto, so it is NOT a
   stacking context and its positioned children resolve against the root. Inside
   it, .background_webgl (the canvas) is z-index 5 and .wrapper_hero (the h1, the
   聯絡我們 button, the SCROLL DOWN caption) is z-index 15. Nothing in the whole
   document occupies 6 through 14, so z-index 10 puts the mascot provably in
   front of the canvas and provably behind the copy. The canvas is opaque
   (getContext("2d", {alpha:false}) and it fillRects the brand pink every frame),
   so anything behind it would simply not exist.

   pointer-events:none is REQUIRED, not cosmetic. .wrapper_hero fills the entire
   hero and wins the hit test at z-index 15, but the mascot must not intercept
   anything either: it overlaps nothing clickable at any measured viewport, and
   the canvas string-pluck FX listens on window, so leaving it transparent to
   the cursor costs nothing and guarantees the button stays reachable.

   POSITIONING. Absolute, not fixed. position:fixed does work here (no ancestor
   of the hero carries a transform, verified) which is exactly the trap: it would
   pin the mascot to the viewport while the hero scrolled away. Absolute plus the
   hero's own overflow:hidden clips it instead, so it can never bleed into the
   section below.

   Do NOT move this element inside .background_webgl or #strings-wrap. The former
   is scaled 1 -> 1.1 by a Webflow IX2 scroll interaction at >=992px only; the
   latter is scaled 0.58-0.94 and lives in an inflated coordinate space.

   The desktop anchor is the CIRCLE CENTRE (50%) rather than the right edge,
   because the ring is horizontally centred at every width. The min() clamp stops
   it running past the right edge between 992 and ~1100px wide, where the hero's
   overflow:hidden would silently chop the heart-shaped tail off. Below 992 the
   ring stops growing with the viewport (its size factor jumps 0.30 -> 0.43), so
   the right gutter becomes the only free space and the rules switch to
   right-anchored. Every offset is in px and anchored to the BOTTOM: below 480px
   .background_webgl carries margin-top:-10em at a 1vw font-size, so the canvas
   sits 10vw above the hero and anything expressed as a percentage of the hero
   height, or measured from its centre, would be 10vw wrong on phones.

   --mascot-max-width is written onto :root by build.mjs from content.json, so he
   can resize the character without touching CSS. It is a CEILING: each
   breakpoint keeps its own measured value and takes whichever is smaller. Note
   the source art is only 636px wide, so 318px is the 2x-crisp ceiling and 212px
   the 3x one. A bigger mascot needs a bigger export, not a bigger number here.
   --------------------------------------------------------------------------- */
.mascot_wrap {
  position: absolute;
  z-index: 10;                 /* above .background_webgl (5), below .wrapper_hero (15) */
  pointer-events: none;
  /* 13.9vw is the measured 200px at 1440 expressed against the viewport, so the
     character tracks the ring instead of standing still while it grows: the ring
     radius goes 269.9 -> 324.1 (+20.1%) from 1440 to 1920, and a fixed 200px
     offset left it marooned in an empty right third at the wider size. */
  left: min(calc(50% + 13.9vw), calc(100% - 340px));
  bottom: 80px;
  /* 20.8vw reproduces the measured 300px at 1440. The 318px ceiling is the hard
     limit of the 636px source at devicePixelRatio 2; above it the art goes soft.
     Growing past that needs a 1272x1598 re-export, not a bigger number here. */
  width: min(clamp(260px, 20.8vw, 318px), var(--mascot-max-width, 318px));
  /* The character's opaque ink ends at y=686 of the 799px frame (the baked
     contact shadow under the feet), so the pivot sits at 86%, near its own
     bottom centre. That is what makes the cursor tilt read as leaning rather
     than sliding. */
  transform-origin: 50% 86%;
  will-change: transform;
}

.mascot_body {
  display: block;
  width: 100%;
  height: auto;                /* the body image alone gives the wrapper its height */
}

/* 101x111 of a 636x799 frame; top 392, left 77 and 289. */
.mascot_eye {
  position: absolute;
  top: 49.0613%;
  width: 15.8805%;
  height: 13.8924%;
  will-change: transform;
}
.mascot_eye--l { left: 12.1069%; }
.mascot_eye--r { left: 45.4403%; }

@media screen and (max-width: 991px) {
  .mascot_wrap {
    left: auto;
    right: 70px;
    bottom: 100px;             /* buys the vertical gap to the SCROLL DOWN caption */
    width: min(240px, var(--mascot-max-width, 300px));
  }
}
@media screen and (max-width: 767px) {
  .mascot_wrap {
    left: auto;
    right: 30px;
    bottom: 80px;
    width: min(220px, var(--mascot-max-width, 300px));
  }
}
@media screen and (max-width: 479px) {
  .mascot_wrap {
    left: auto;
    /* was 4px, which left the heart tail 3.67px off the screen edge with no
       margin for a curved-edge display. Every other breakpoint gets 30px+. */
    right: 12px;
    /* The PNG carries 113px of transparent padding under the character, so with
       a bottom anchor the visible ink rises as the width grows. 185px clears the
       h1 by 51.6px and the full-width button by 134.6px. */
    bottom: 185px;
    width: min(200px, var(--mascot-max-width, 300px));
  }
}

/* mascot.js does not start its loop at all under prefers-reduced-motion, and it
   clears will-change itself. This is the belt-and-braces case where the script
   never runs: no promoted layer, and the eyes stay at their rest position. */
/* Phones in LANDSCAPE. Below 992px the site's <header> becomes position:fixed,
   z-index 99, background rgb(198,87,130), i.e. fully OPAQUE; above 992px it is
   rgba(0,0,0,0), which is why this never showed up in desktop checks. The mascot
   is bottom-anchored in px, so its top is a pure function of viewport height
   (991 band: H-401.5, 767 band: H-356.4). On a short viewport that rises above
   the header's bottom edge and the opaque bar paints straight over it at z-index
   99 vs 10, decapitating the character and leaving the heart tail floating
   beside the Menu button like a second logo. Every phone landscape height (375
   to 430) is well inside this, and the cutoff has ~65px of headroom over the
   measured 495px failure point. */
@media screen and (max-width: 991px) and (max-height: 560px) {
  .mascot_wrap { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .mascot_wrap,
  .mascot_eye {
    will-change: auto;
    transform: none !important;
  }
}

/* ---------------------------------------------------------------------------
   8. The mascot IS the hero. 2026-07-29.

   His call: "remove the interactive component canto8 had and make the mascot
   the main thing on the hero page as big as the component replacing it
   completely." So .background_webgl and its script are deleted in build.mjs and
   the character now occupies the space the ring used to.

   CENTRING WITHOUT transform. mascot.js owns the element's `transform` every
   frame (lean, bob, breathe), so any translate(-50%) here would be overwritten
   on the first animation frame. Width is a custom property so the height can be
   derived from the art's own 636x799 ratio, and the vertical offset is a
   multiple of that width rather than a percentage of the hero.

   The offset is -0.62 x width, not the -0.54 that would centre the frame. The
   character's opaque ink ends at 686 of the 799px frame, so centring the FRAME
   sits the character low, and more importantly its feet would then reach the
   white h1. .wrapper_hero is z-index 15 and the mascot is 10, so an overlap
   means white headline over a white body: invisible text. The extra lift keeps
   the ink clear of the headline. Verified by measurement, not by eye.
   --------------------------------------------------------------------------- */
.hero { background-color: var(--blue); }   /* the canvas used to paint this */

.mascot_wrap {
  --mw: min(clamp(300px, 36vw, 560px), var(--mascot-max-width, 560px));
  left: 0;
  right: 0;
  margin-inline: auto;
  top: 50%;
  bottom: auto;
  width: var(--mw);
  height: calc(var(--mw) * 799 / 636);
  /* VERTICAL optical centring, 2026-07-31. He reported the mascot is not
     vertically in the middle and he is right: the old flat -0.70 lift parked the
     character's optical centre 46 to 77px ABOVE the viewport centre at every
     width (measured live at 5 viewports).

     Per-row alpha profile of mascot-body@2x.png (count of pixels with a >= 128
     per row, 10px noise floor): ink spans 0.0013 to 0.8442 of the frame height,
     mass centroid at 0.4851. Frame height is 1.2563 x width, so:
       centred:      margin-top = -(0.4851 * 1.2563)W = -0.6094W
       ink bottom:   sits at 50svh + (0.8442*1.2563 - 0.6094)W = 50svh + 0.4513W

     TRUE centring overlaps the white h1 on short viewports (46px INTO it at
     1440x800), and .wrapper_hero is z-index 15 over the mascot's 10, so overlap
     means white headline on a white body: invisible text. Hence the min(): the
     centred position, OR as low as the h1 allows, whichever is higher. The h1
     top measures ~211px up from the viewport bottom on the common heights, so
     the constraint keeps the ink bottom >= ~21px clear of it (226 = 218 + margin; 218 is the tightest h1 offset measured, at 991x800;
     verified live afterwards, not assumed). Tall viewports get true centring;
     short ones degrade gracefully to "as centred as fits". */
  margin-top: min(calc(var(--mw) * -0.6094),
                  calc(50svh - 226px - var(--mw) * 1.0606));

  /* HORIZONTAL optical centring. He reported the mascot reads left of centre and he
     is right: `margin-inline:auto` centres the FRAME, and the frame is not the
     character. The heart sits on a stalk out to the upper right and reaches the
     right edge, so the opaque bounding box is almost centred (51.25%) while the
     character's visual weight is nowhere near it.

     Measured on mascot-body@2x.png, 320-column alpha profile, threshold >= 128:
       opaque bbox centre   51.25%
       whole-mass centroid  42.53%   <- shift +0.075 to centre
       body blob alone      39.20%   <- shift +0.108 to centre
     Using the whole-mass figure: it centres what the eye actually weighs without
     throwing the heart off the right edge.

     This uses the INDEPENDENT `translate` property, NOT `transform`. mascot.js
     writes style.transform every frame and would overwrite a transform declared
     here; `translate` composes with it instead. Verified mascot.js never touches
     style.translate. */
  translate: calc(var(--mw) * 0.075) 0;
}

@media screen and (max-width: 991px) {
  .mascot_wrap { --mw: min(clamp(260px, 46vw, 420px), var(--mascot-max-width, 560px)); right: 0; }
  /* the old flat -0.78 lift is gone: the base min() formula already adapts to
     the viewport height, which is what that override was hand-approximating */
}
@media screen and (max-width: 767px) {
  .mascot_wrap { --mw: min(clamp(240px, 58vw, 380px), var(--mascot-max-width, 560px)); right: 0; }
}
@media screen and (max-width: 479px) {
  .mascot_wrap { --mw: min(clamp(220px, 66vw, 320px), var(--mascot-max-width, 560px)); right: 0; bottom: auto; }
}

/* ---------------------------------------------------------------------------
   9. Product cards: his photo fills the card, the copy sits on top. 2026-07-29.

   "Fill the whole card like from top to bottom even backgrounding the text
   remove the pink completely."

   Webflow ships .product_card{background-color:var(--blue)} and --blue is the
   brand token, which our colour pass turned pink. That is the pink he means, so
   the card background becomes a dark neutral: it is only ever seen for the
   instant before the photo decodes, and a pink flash would defeat the point.

   .product_visual had a FIXED height (21.5em/34em/20rem per breakpoint) and sat
   in normal flow above the title. It is now absolutely filling the card with the
   title stacked over it. Layer order is photo 0, scrim 1, title 2, tagline 3.

   The scrim is not decoration. The copy is white and the photos run from a
   near-white petri dish to a dark newborn shot, so without it the text is
   illegible on at least two of the six. It is neutral dark, not pink.
   --------------------------------------------------------------------------- */
.product_card {
  background-color: #14171c;
  /* The card USED to get its height from .product_visual sitting in flow at a fixed
     21.5em. Pinning the content absolutely to fill the card took that away and the
     card collapsed to 149px tall, so the photo filled a dark letterbox. An explicit
     ratio gives it a definite height again, and 3:4 suits his photography, which is
     portrait at 933x1400. height:auto is required because Webflow sets height:100%,
     and a percentage height would win over aspect-ratio. */
  height: auto;
  aspect-ratio: 3 / 4;
}

.product_card > .product_content {
  /* absolute, not height:100%. .product_item has no definite height, so height:100%
     resolved to auto and the photo only filled the CONTENT box, measured at
     fillsCard=false on all six cards. Pinning to the card guarantees the fill
     whatever the row height turns out to be. */
  position: absolute;
  inset: 0;
  margin: 0;
  display: flex;
  flex-flow: column;
  justify-content: flex-end;
}

.product_visual {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
}

/* THE COPY BLOCK. Fixed 2026-07-29 after he asked "why did you remove the text on
   the card". Nothing was removed: .about_product holds BOTH the title and the
   description, and only .product_title_card had been given a stacking context, so
   the absolutely-positioned photo painted straight over .text_desc. The description
   was in the DOM, visible, opacity 1, and invisible on screen.

   Two things were wrong, not one:

   1. z-index. Lift .about_product, the wrapper, not just the title. An earlier
      z-index on the title alone is exactly what hid its sibling.
   2. Position. Webflow's own card is `display:flex; flex-flow:column` with the
      visual in flow at a fixed 21.5em and .about_product AFTER it, plus
      `.about_product{margin-top:auto}` at the narrow breakpoints. So the copy
      always belonged at the BOTTOM. Pinning the photo took the visual out of
      flow, which left the copy as the only in-flow child and floated it to the
      top. `margin-top:auto` puts it back where the original had it.

   His own spec file agrees, which is the tiebreaker:
   "服務名稱放左下方 ... 短描述放在服務名稱下方，建議控制在三行內."
   --------------------------------------------------------------------------- */
.about_product {
  position: relative;
  z-index: 2;
  margin-top: auto;          /* bottom-anchored, as Webflow's own narrow breakpoints do */
  text-shadow: 0 1px 12px rgba(10, 12, 15, 0.55);
}

.product_visual::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Weighted to the BOTTOM, matching where the copy now sits. The previous
     top-weighted ramp was built from a measurement taken while the copy was
     wrongly floated to the top of the card, so it was darkening the wrong half.
     The faint top stop keeps the tagline and the arrow legible in .tag_link. */
  background: linear-gradient(
    to top,
    rgba(10, 12, 15, 0.90) 0%,
    rgba(10, 12, 15, 0.78) 22%,
    rgba(10, 12, 15, 0.42) 42%,
    rgba(10, 12, 15, 0.08) 62%,
    rgba(10, 12, 15, 0.34) 100%
  );
}

.product_photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tag_link { z-index: 3; }

/* ---------------------------------------------------------------------------
   10. 專業 x 溫暖 x 陪伴 in the brand pink. 2026-07-29, his experiment.
   Measured contrast against the silver is reported to him separately; this is
   the change he asked for, not a recommendation.
   --------------------------------------------------------------------------- */
.team_motto { color: var(--blue); }

/* ---------------------------------------------------------------------------
   11. Silver replaces EVERY remaining near-black surface. His call, 2026-08-01:
   "change everyone of the black shit like the ones we made silver to silver
   throughout the whole site."

   Same treatment as section 3: the 90deg multi-stop sheen, dark ink for text
   that used to be white on the black. Scoped per surface because --black is
   also a TEXT colour in five places (see the 2026-07-26 gotcha): a global
   variable swap would invert those.

   Surfaces measured at rgb(21,21,21), 2026-08-01, twelve pages probed:
     .footer                     every page
     body.products_page          the services overview page
     .products_scroll            its main wrapper
     .hero_section.product_template_page   the six service page heroes
     section.opertional          the six service pages' capability section
     section.black_cards         the company page band (mission included)

   NOT silvered on purpose: .product_card's #14171c is the dark neutral behind
   his photography ("remove the pink completely"), only seen while photos decode.
   --------------------------------------------------------------------------- */
.footer,
body.products_page,
.products_scroll,
.hero_section.product_template_page,
section.opertional,
section.black_cards {
  background-color: var(--silver-base);
  background-image: linear-gradient(
    90deg,
    #8a929a 0%,
    #bcc3ca 7%,
    #ffffff 14%,
    #c8ced4 21%,
    #868e96 33%,
    #e9edf0 46%,
    #ffffff 53%,
    #aeb6bd 66%,
    #888f97 80%,
    #d2d8dd 92%,
    #949ca4 100%
  );
}

/* Footer text: broad ink flip. The Subscribe button's label is an <input>, so
   it keeps its own white-on-pink; images are untouched. */
.footer :is(div, span, a, h1, h2, h3, p) {
  color: var(--silver-ink);
}
/* The footer's e-mail field wrote white on transparent; ink + a visible border. */
.footer input.input_subscribe,
.footer input[type="email"],
.footer input[type="text"] {
  color: var(--silver-ink);
  border-color: rgba(27, 32, 38, 0.35);
}
/* The big footer logo is the WHITE wordmark image: invisible on silver.
   Swap the rendered source for the pink variant (same trick class as the
   header swap in section 5, but this one is an <img>, hence content:url). */
.footer .logo_footer .image {
  content: url("../../images/brand-logo-pink.png");
}

/* Services overview page: the h1 flips to ink. Card copy stays WHITE, it sits
   on his photographs behind a scrim, not on the silver. */
.products_page .products_heading .h1 { color: var(--silver-ink); }

/* Service page heroes: title, intro and the three summary columns. */
.hero_section.product_template_page :is(h1, .p_gen, .title_cap, .text_cap, .sub_caption, .tag_txt div) {
  color: var(--silver-ink);
}

/* Service page capability section: heading, eyebrow, left paragraph, the six
   list labels. */
section.opertional :is(h2, div) { color: var(--silver-ink); }

/* Company band: section heading and the mission block flip to ink. The four
   cards keep their own pink background and white text. */
section.black_cards :is(.h2.about_spec, .p_specific_about) { color: var(--silver-ink); }
section.black_cards > * > :is(.tag_txt, .mission_box, .flex_mission) :is(div, h2) { color: var(--silver-ink); }
section.black_cards .p_gen { color: var(--silver-ink); }
.black_cards .content_point .p_gen { color: #fff; }  /* card body text stays on pink */
.black_cards .title_point_ab div { color: #fff; }
