@charset "utf-8";
/* ==========================================================================
   CP Cards — unified responsive stylesheet (pilot)
   Replaces style-cpcards-desktop.css + style-cpcards-mobile.css +
   slideoutmenu-style.css on CONVERTED pages only. Unconverted pages keep
   loading the old pair via navigation/header.php.

   Breakpoints:
     base            desktop design, fluid, canvas max-width 1200px
     max-width 900px tablet squeeze — columns stack, logos shrink
     max-width 640px phone — desktop chrome hidden, mobile chrome shown
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Base elements & typography (desktop values, from style-cpcards-desktop)
   -------------------------------------------------------------------------- */

html { height: 100%; scroll-behavior: smooth; }

body {
  border: 0;
  height: 100%;
  margin: 0 auto;
  padding: 0;
  /* bleeds to match #canvasWrapper's own background (line ~159) so the
     centered canvas has no visible seam against the browser edges at
     wide viewports, instead of the old flat black letterboxing.
     #colaboveheader now bleeds its own teal background directly (see
     its rule below), so there's no longer a need to special-case the
     top strip here — that was only ever a workaround for two sprite
     images (header-england.webp / header-email-us-sprite.webp) that
     no longer exist in this markup. */
  background-color: #efefef;
  color: #000000;
}

/* Above the 1400px canvas cap the whole background turns white — BOTH the
   side gutters (body) and the 1400px page sheet itself (#canvasWrapper).
   The first version of this rule only whitened body, on the theory that
   keeping #canvasWrapper at #efefef would read as "a defined sheet on a
   white surround". In practice that just put a grey slab back in the
   middle of the viewport, which is the opposite of the intent — so the
   sheet goes white too and there is no grey anywhere past 1400px.
   The header and footer bands are unaffected and keep running edge to
   edge: they set their own width from var(--vw) with a negative margin
   (see #colheader / #colaboveheader / #colheadermenu / .mainfooter), so
   they span the full viewport regardless of what is behind them. Same
   goes for the content bands (.headingfullwidth / .weoffercontainer /
   .weofferheading) — those still bleed, and they cover roughly 2/3 of
   this page's height, so most of what reaches the viewport edge is a
   section colour rather than background at all.
   1401px, not 1400px, so the change happens once the viewport is
   genuinely wider than the canvas — at exactly 1400px there are no
   gutters to colour.
   The matching #canvasWrapper override is NOT here — it lives directly
   after that element's own rule further down, because both selectors are
   a bare id (same specificity) and the later one in source order wins.
   Put here, it silently lost. */
@media (min-width: 1401px) {
  body { background-color: #ffffff; }
}

img { border: 0 none; -ms-interpolation-mode: bicubic; }

/* fluid images site-wide: width/height attributes keep the aspect ratio
   (no layout shift) while max-width stops overflow on small screens */
#contentWrapper img,
#pageFooter img,
#colheader img,
#colaboveheader img,
#colheadermobile img {
  max-width: 100%;
  height: auto;
}

td { background: transparent; border: 0; margin: 0; padding: 0; }

p {
  margin: 0 0 10px 0;
  font-family: Lota, sans-serif;
  font-size: 20px; /* mainfont */
  font-weight: 300;
  line-height: 28px;
  color: #555555; /* mainfontcolour */
}

strong {
  font-family: Lota, sans-serif;
  font-weight: bold;
  color: #555555;
}

/* headings (global element styles, from style-cpcards-desktop) */
/* text-align:center is site-wide by request — every heading centres, the
   way the h1 on the homepage always did via its own inline style. Set on
   the element selector rather than on each heading class so it reaches
   headings that carry no class at all, and so a section that genuinely
   needs left-aligned headings can opt out with a single more-specific
   rule instead of the alignment having to be repeated everywhere. */
h1, h2, h3, h4, h5, h6 {
  margin: 0;
  padding: 0;
  font-family: Avenir Next, sans-serif;
  font-weight: bold;
  text-align: center;
}
h1 { font-size: 24px; line-height: 1.5em; font-style: normal; padding-bottom: 5px; padding-top: 15px; }
h2 { font-size: 16px; line-height: 1.5em; font-style: normal; padding-bottom: 5px; padding-top: 15px; }
h3 { font-size: 12px; line-height: 1.5em; font-style: normal; padding-bottom: 5px; padding-top: 15px; }

/* Swept in with the h1–h6 centring above: things that read as headings but
   aren't marked up as one, so the rule on the element selector never
   reached them and they were the only headings left sitting flush left.
     p.price-heading      "How to Order" above the numbered order steps
                          (9 niche pages); .price-sub is its own subtitle
                          line and has to follow it or the pair splits
     .comp-card .subtitle "Made of pixels (dots of colour)" — sits directly
                          under a now-centred h3 inside the same card
     div.heading          the emphasised statements in
                          postal-proof-decline-statement
   Fixing the markup would be the better answer for most of these, but that
   is a separate job across the pages themselves; this keeps the alignment
   consistent without touching content. */
p.price-heading,
p.price-sub,
.comp-card .subtitle,
div.heading {
  text-align: center;
}

/* FAQ questions stay left, over their left-aligned answers — a centred
   question above a flush-left answer reads as two unrelated blocks.
   Deliberately qualified with h3 rather than a bare .faq-question:
   raster-vs-vector-images has its own page-scoped .faq-question on
   <button> accordion triggers, and an unqualified selector here would
   centre those too. */
h3.faq-question { text-align: left; }

ul { list-style-type: square; margin: 1em 0 1em; padding: 0; padding-left: 2em; }
form { margin: 0; padding: 0; }

/* links (global, from style-cpcards-desktop): grey + bold, green on hover */
a { -moz-outline: none; }
a:link, a:visited, a:active {
  line-height: 28px;
  font-weight: bold;
  font-style: normal;
  text-decoration: underline;
  color: #555555; /* mainfontcolour */
  border-color: #7D7D7D;
}
a:hover {
  color: #00C800;
  border-color: #7D7D7D;
}

small   { font-family: Lota, sans-serif; font-size: 10px; line-height: 28px; font-weight: normal; font-style: normal; }
small10 { font-family: Lota, sans-serif; font-size: 10px; line-height: 28px; font-weight: normal; font-style: normal; }
small12 { font-family: Lota, sans-serif; font-size: 12px; line-height: 28px; font-weight: normal; font-style: normal; }
small14 { font-family: Lota, sans-serif; font-size: 14px; line-height: 28px; font-weight: normal; font-style: normal; }
small16 { font-family: Lota, sans-serif; font-size: 16px; line-height: 28px; font-weight: normal; font-style: normal; }
small18 { font-family: Lota, sans-serif; font-size: 18px; line-height: 28px; font-weight: normal; font-style: normal; }
small20 { font-family: Lota, sans-serif; font-size: 20px; line-height: 28px; font-weight: normal; font-style: normal; }
small22 { font-family: Lota, sans-serif; font-size: 22px; line-height: 28px; font-weight: normal; font-style: normal; }
small24 { font-family: Lota, sans-serif; font-size: 24px; line-height: 28px; font-weight: normal; font-style: normal; }
big     { font-family: Lota, sans-serif; font-size: 24px; line-height: 28px; font-weight: bold; font-style: normal; }
big26   { font-family: Lota, sans-serif; font-size: 26px; line-height: 28px; font-weight: bold; font-style: normal; }

span.cyan       { color: #1B4965 !important; }
span.darkteal   { color: #123A52 !important; }
span.softskyblue{ color: #9DCEF9 !important; }
span.coolgrey   { color: #ECEFF1 !important; }
span.warmsand   { color: #F5F5F5 !important; }
span.softmint   { color: #C8E6C9 !important; }
span.darkgrey   { color: #37474F !important; }
span.softgrey   { color: #DAD7D9 !important; }

.cyan-background       { background-color: #1B4965; }
.darkteal-background   { background-color: #123A52; }
.softskyblue-background{ background-color: #9DCEF9; }
.coolgrey-background   { background-color: #ECEFF1; }
.warmsand-background   { background-color: #F5F5F5; }
.softmint-background   { background-color: #C8E6C9; }
.darkgrey-background   { background-color: #37474F; }

@media screen and (min-width: 1025px) {
  .imagehover { opacity: 1.0; }
  .imagehover:hover { opacity: 0.8 !important; }
  .imagehovermenu { opacity: 1.0; }
  .imagehovermenu:hover { opacity: 0.6 !important; }
}


/* --------------------------------------------------------------------------
   2. Visibility utilities — now driven by VIEWPORT width, not device width
   -------------------------------------------------------------------------- */

.desktop-only { display: block !important; }
.mobile-only  { display: none !important; }

@media (max-width: 640px) {
  .desktop-only { display: none !important; }
  .mobile-only  { display: block !important; }

  /* Exception: the top nav row + "Our Cards" dropdown (#colheadermenu)
     stays visible at every width instead of handing off to a phone
     menu — there's no separate mobile menu any more, so its old
     phone-only counterpart (#colmenubuttons + the mobile special-offer
     banner) is hidden instead. The big desktop logo header (#colheader)
     stays properly hidden on phones — untouched by either override. */
  .desktop-only:has(#colheadermenu) { display: block !important; }
  .mobile-only:has(#colmenubuttons) { display: none !important; }

  /* Exception: the flag/wording/heart + email strip (#colaboveheader)
     also stays visible at every width, same reasoning as above — it's
     wrapped in its own .desktop-only (separate from #colheader's) so
     it can be kept visible independently. */
  .desktop-only:has(#colaboveheader) { display: block !important; }
}


/* --------------------------------------------------------------------------
   3. Page scaffolding
   -------------------------------------------------------------------------- */

#pageBodyWrapper { clear: both; }

#canvasWrapper {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  background-color: #efefef;
  /* was overflow:hidden — removed so full-bleed sections (.headingfullwidth,
     .homeheadingfullwidth, .weoffercontainer) can break out to the true
     viewport edge via their own width:100vw + negative-margin rule below,
     instead of being clipped back to the canvas width. The flexslider
     photo-gallery carousel used to depend on THIS overflow:hidden as an
     accidental backstop (it has no clip of its own) — confirmed via a
     genuine 35px horizontal-overflow regression when this was removed
     without also fixing that; fixed at the source instead by adding
     overflow:hidden directly to .flexslider (assets/flexslider/flexslider.css),
     which is where a slider's clipping belongs regardless of this change. */
  /* positioning context for the mobile special-offer badge (see the
     ≤640px #colheaderspecialoffer override) to escape #colheadermenu's
     own positioning context and land in the header row above instead */
  position: relative;
}

/* Past the 1400px cap the sheet turns white too, so nothing grey is left
   anywhere beyond that width (the gutters are handled by the body rule
   near the top of this file). This has to sit AFTER the rule above, not
   next to its body counterpart: `#canvasWrapper` is the same specificity
   in both places, so whichever comes last in source order wins — placed
   up there it was overridden by the #efefef above and had no effect. */
@media (min-width: 1401px) {
  #canvasWrapper { background-color: #ffffff; }
}

#contentWrapper {
  width: 100%;
  float: left;
  font-family: Lota, sans-serif;
  font-size: 20px !important; /* mainfont */
  font-weight: 300;
  line-height: 28px;
  color: #555555;
  padding: 0;
}


/* --------------------------------------------------------------------------
   4. Desktop header & navigation (shown above 640px, fluid)
   -------------------------------------------------------------------------- */

/* bleeds its teal background to the viewport edge, same mechanism as
   #colheader/#colheadermenu/.mainfooter — width:100vw + negative
   margin, then left/right padding adds back the gutter width so
   .colaboveheaderbrand/.headeremailusbanner stay canvas-aligned.
   The +20px on top of the gutter-compensation term (matching
   #colheadermenu's own base padding) is a minimum margin: below the
   1400px cap the gutter term is 0, so without it the flag icon/email
   text would sit flush against the true browser edge as the window
   narrows. */
#colaboveheader {
  /* Deliberately DARKER than the shared heading colour #123A52 (which the
     nav underline and the .weofferheading bars still use). Once the header
     bands either side of this strip became the navy #1B4965, the old teal
     was too close to it in tone to read as a separate band — the two
     differ by only 1.09:1 in relative luminance, so the boundary all but
     disappeared. This deepened teal sits at 1.6:1 against the navy, which
     is enough to see the edge, while keeping the same hue so it still
     belongs to the palette. White text on it goes from 10.1:1 to 15.2:1,
     so legibility only improves. */
  background-color: #032C2C;
  width: var(--vw, 100vw);
  position: relative;
  z-index: 5600;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 20px);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 20px);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* flag icon + wording + heart icon, grouped into one flex item so they
   stay tightly clustered together (a single small gap each side of
   the text) while #colaboveheader's own justify-content:space-between
   still puts the deliberate large gap between this whole cluster and
   the email link on the right. */
.colaboveheaderbrand {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* "Proudly printed in England" flag + heart.

   The heart beats continuously — two quick pumps then a rest, the shape of
   an actual pulse rather than a single even throb, which is what stops it
   reading as a generic "pop" animation. transform-origin:center so it grows
   about its own middle and the 8px gaps either side stay put; nothing in
   the row moves, since a transform never affects layout.

   No hover behaviour on either image, per request — the beat runs on its
   own and neither the flag nor the heart responds to the pointer. */
@keyframes brandheartbeat {
  0%, 28%, 70%, 100% { transform: scale(1); }
  14%, 42%           { transform: scale(1.16); }
}

.colaboveheaderbrand .brandheart {
  transform-origin: center center;
  animation: brandheartbeat 1.6s ease-in-out infinite;
}

/* Motion is decorative here, and this one runs unprompted and forever, so
   it stops entirely for anyone who has asked their system to reduce
   motion. The heart still shows, just still. */
@media (prefers-reduced-motion: reduce) {
  .colaboveheaderbrand .brandheart { animation: none; }
}

/* matches the site's global heading font/weight (see h1-h6, line ~79)
   rather than the body-copy font (Lota), per request — white for
   contrast against #colaboveheader's dark teal background. */
.colaboveheaderwording {
  font-family: Avenir Next, sans-serif;
  font-weight: bold;
  font-size: 19.2px; /* 20% bigger than the original 16px, per request */
  color: #ffffff;
  white-space: nowrap;
}

/* real text (was an image sprite with a background-position hover
   swap) — same recipe as the #colheadermenu nav buttons below: same
   font/weight, white → mint colour shift, animated centre-out
   underline on hover/focus. Scoped under #colaboveheader (ID+class,
   specificity 1,1,0) for the same reason the nav buttons are scoped
   under #colheadermenu: the global "a:link { color:#555 }" rule
   (0,1,1) otherwise beats a bare .headeremailusbanner (0,1,0)
   regardless of source order. */
#colaboveheader .headeremailusbanner {
  display: inline-block;
  position: relative;
  width: auto;
  /* bottom padding trimmed from 10px to 2px so the "We aim to reply..."
     subtext below sits noticeably closer, per request */
  padding: 8px 6px 2px 4px;
  font-family: "Avenir Next", Lota, sans-serif;
  /* bigger + bolder than the nav-button recipe this was originally
     copied from (600/clamp(11,...,22)) — scaled by ~1.25x and bumped
     to 700, per request, for every width down to the mobile clamp
     below. */
  font-weight: 700;
  font-size: clamp(14px, calc(2.44vw - 0.81px), 27.5px);
  line-height: 1.3;
  color: #ffffff;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.2s ease, transform 0.2s ease;
}
#colaboveheader .headeremailusbanner::before {
  content: "";
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: 2px;
  height: 2px;
  background: #C8E6C9;
  border-radius: 2px;
  transform: scaleX(0);
  transition: transform 0.25s ease;
}
#colaboveheader .headeremailusbanner:hover,
#colaboveheader .headeremailusbanner:focus-visible {
  color: #C8E6C9;
}
#colaboveheader .headeremailusbanner:hover::before,
#colaboveheader .headeremailusbanner:focus-visible::before {
  transform: scaleX(1);
}

/* stacks the email link over its "We aim to reply..." subtext as one
   flex item, so #colaboveheader's own space-between still puts the gap
   between this whole group and .colaboveheaderbrand rather than
   between the two lines.
   align-items:flex-end (rather than the row's own align-items:center,
   which this overrides for this one item's cross-axis — note this is
   the group's OWN align-items, controlling how ITS children, the email
   link and subtext, line up inside it — not align-self) right-aligns
   the narrower subtext under the wider email line above it, per
   request, instead of centring it.
   No more align-self:flex-start: that used to anchor this item to the
   row's top edge, needed only because positionHeaderPhone() used to
   deliberately overlap the (absolutely positioned) phone number link
   into this row's own top and then push this group down far enough to
   clear it — which left it pinned flush to the row's bottom with no
   breathing room below, reading as "too low" rather than centred.
   positionHeaderPhone() now reserves the phone's room in the logo box
   above instead (growing #colheader/#colheadermobile, whichever is
   visible, rather than overlapping into #colaboveheader at all), so
   this item is free to use the row's own align-items:center like
   .colaboveheaderbrand already does, and both end up genuinely
   vertically centred, top-to-bottom, in an unpadded row. */
.headeremailgroup {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 0 auto;
}

/* font-size is set precisely by matchEmailSubtextWidth() in
   jquery_functions.js so this line's rendered width always exactly
   matches the "Email: sales@cpcards.co.uk" line above it — the value
   here is only a fallback for the instant before that JS runs (or if
   it doesn't run at all), sized to roughly the right ballpark so
   there's no visible flash of mis-sized text.

   ONE phrase at every width, per request: "We aim to reply within 10
   minutes 9-5 Mon-Fri" now shows at all viewport widths, shrinking as
   far as it has to in order to keep fitting on one line as the page
   narrows. There used to be a second, shorter variant
   (.headeremailsubtext-short, "Reply within 10 mins") swapped in below
   640px precisely BECAUSE the full phrase has to shrink so hard to
   match the (much shorter) email line's width on phones — both spans
   and the display:none swap between them are gone now. See
   matchEmailSubtextWidth() for the sizing itself and for why there's
   deliberately no minimum font-size floor here.

   text-align:right (was center) per request — belt-and-braces
   alongside .headeremailgroup's own align-items:flex-end above, since
   this element's own box already shrinks to ~exactly its rendered text
   width (via the JS font-size match), so either one alone would
   mostly already look right-aligned; both together make sure there's
   no stray centring if that box and its text width ever drift apart
   by a fraction of a pixel. */
.headeremailsubtext {
  display: block;
  font-family: "Avenir Next", Lota, sans-serif;
  font-weight: 600;
  font-size: clamp(8px, calc(0.8vw + 4px), 12px);
  line-height: 1.3;
  color: #ffffff;
  padding: 0px 6px 10px 4px;
  white-space: nowrap;
  text-align: right;
}

/* phone number — pinned directly under the special-offer badge and
   right-aligned with it. top/right are set by positionHeaderPhone() in
   jquery_functions.js, not fixed CSS values: the badge itself is
   dynamically positioned/sized per viewport (see alignHeaderSpecialOffer()
   in that same file), so a static offset here would drift out of sync
   with it. That same function also reserves room below this link (a
   margin-top on .headeremailgroup, computed from this link's actual
   rendered height) so the generous padding below never overlaps the
   "Email:" link underneath — an earlier version tried keeping this
   link in normal document flow instead, specifically to sidestep that
   overlap risk without needing JS to manage it, but that meant it
   could only ever be the first line of the *email block*, one
   pre-existing ~42px gap below the badge — not "directly under" it.
   Padding is asymmetric — small on top (4px, so the visible text sits
   close to the badge — the "directly under" part), generous on the
   bottom (extending into the space the JS above reserves) — so the
   ~44px+ minimum touch target size (WCAG 2.5.5 / Apple HIG) comes from
   safely growing the hit area downward, not from a gap against the
   badge above it.
   margin-right cancels padding-right's effect on positioning only (not
   its effect on hit-area size) — without it, the padding would push
   the *visible text* left of the badge's edge instead of just growing
   the invisible hit area, breaking the right-alignment.
   Scoped under #colaboveheader (ID+class) for the same reason as
   .headeremailusbanner above: the global "a:link { color: #555 }"
   rule otherwise beats a bare .headerphonebanner regardless of source
   order. */
#colaboveheader .headerphonebanner {
  position: absolute;
  z-index: 5250;
  padding: 4px 12px 26px 12px;
  margin-right: -7px;
  font-family: "Avenir Next", Lota, sans-serif;
  font-weight: 700;
  /* 10% bigger than the shared nav-button-style recipe this was
     originally sized from, per request */
  font-size: clamp(12.1px, calc(1.43vw + 3.3px), 16.5px);
  line-height: 1.3;
  color: #ffffff;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.2s ease;
}
#colaboveheader .headerphonebanner:hover,
#colaboveheader .headerphonebanner:focus-visible {
  color: #C8E6C9;
}

/* ==========================================================================
   TEMPORARY — header phone number hidden (requested 11 Aug 2026).
   To bring the phone number back, delete this one rule (or comment it out).
   Nothing else was changed: the markup, the link and all the positioning
   JS are untouched, so removing this restores the previous behaviour
   exactly. Kept as display:none rather than visibility:hidden so it also
   leaves no reserved space and is skipped by screen readers.
   ========================================================================== */
#colaboveheader .headerphonebanner { display: none; }
/* ====================== END TEMPORARY phone hide ========================= */

/* bleeds its cyan background to the viewport edge, same mechanism as
   .headingfullwidth/.weoffercontainer — width:100vw + negative margin,
   then left/right padding adds back the gutter width so
   #colheadercpcardslogo's percentage width still resolves against the
   original canvas-equivalent content box. */
#colheader {
  background-color: #1B4965;
  width: var(--vw, 100vw);
  position: relative;
  z-index: 5000;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 10px;
  padding-bottom: 0;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  border-bottom: 0px solid #ffffff;
  min-height: 245px;
  box-sizing: border-box;
}

#colheadercpcardslogo {
  width: 66%;
  max-width: 790px;
  z-index: 5300;
}
#colheadercpcardslogo img { width: 100%; height: auto; }

/* Logo hover — the same gesture as the header's "Get a Quote" pill
   (#colheadermenu .quotepillgroup, further down): same brighten, same
   3px lift with a 1.02 scale, same easing/durations, and the same
   snappy 1px press on click.

   Two deliberate differences, both forced by this being a transparent
   logo rather than a filled button:
     - depth comes from drop-shadow() inside the same filter chain, not
       box-shadow. box-shadow would trace the <img>'s rectangular box and
       paint a hard-edged slab of shadow across the navy band; drop-shadow
       follows the artwork's own alpha, so the shadow hugs the lettering.
       The two drop-shadows mirror the pill's near/far shadow pairing.
     - opacity is pinned to 1, overriding the .imagehover class this image
       carries (0.8 on hover). That fade was the old effect being replaced;
       left in place it would dim the logo while it lifted, which reads as
       two competing gestures.
   transform-origin keeps the scale-up anchored at the left so the logo
   grows away from the page edge rather than into it. */
#colheadercpcardslogo a {
  display: inline-block;
  transform-origin: left center;
  transition: transform 0.15s ease, filter 0.2s ease;
}
#colheadercpcardslogo a:hover,
#colheadercpcardslogo a:focus-visible {
  transform: translateY(-3px) scale(1.02);
  filter: brightness(1.15)
          drop-shadow(0 4px 8px rgba(0, 0, 0, 0.18))
          drop-shadow(0 10px 22px rgba(0, 0, 0, 0.22));
}
#colheadercpcardslogo a:active {
  filter: brightness(0.95) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
  transform: translateY(1px);
}
#colheadercpcardslogo a:hover img.imagehover,
#colheadercpcardslogo a:focus-visible img.imagehover { opacity: 1 !important; }

/* bleeds its cyan background the same way #colheader above does — the
   nav buttons inside stay canvas-aligned via the compensated padding
   (base 20px/10px kept, gutter width added on top). At ≤640px the
   gutter term evaluates to 0 (canvas = 100vw there), so this quietly
   reduces back to the original fixed padding on phones — no separate
   mobile override needed for this specific rule. */
#colheadermenu {
  width: var(--vw, 100vw);
  position: relative;
  float: left;
  background-color: #1B4965;
  z-index: 5250;
  border-bottom: 6px solid #123A52;
  box-sizing: border-box;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 6px;
  padding-bottom: 10px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 20px);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 20px);
}

/* single flex row of all 7 nav buttons inside the menu banner —
   space-between so the row always spans the full 1200px menu width
   edge to edge. Was two separate rows (4 + 3 buttons); merged into one
   per request, with the gap/font-size/padding below all shrunk enough
   that all 7 fit on one line at every width from 900px up (flex-wrap
   is kept as a safety net, not because it's expected to trigger). */
.navrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px clamp(6px, calc(1.1vw - 0.4px), 12px);
  padding: 8px 0 0 0;
}

/* product-line special offer graphic in the header — positioned
   absolute within #colheadermenu, which now bleeds to 100vw (see its
   rule above); "right" needs the same gutter-compensation as that
   rule's padding, otherwise 20px is measured from the true viewport
   edge instead of the canvas edge, letting the banner drift into the
   bleed at wide viewports instead of staying pinned to the canvas. */
#colheaderspecialoffer {
  width: 32%;
  max-width: 380px;
  position: absolute;
  top: -235px;
  right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 20px);
  z-index: 5250;
}

.headerspecialofferloyalty {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-loyalty-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferloyalty:hover { background-position: 0 100%; }

.headerspecialoffergift {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-gift-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffergift:hover { background-position: 0 100%; }

.headerspecialoffermembership {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-membership-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffermembership:hover { background-position: 0 100%; }

.headerspecialofferangling {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-angling-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferangling:hover { background-position: 0 100%; }

.headerspecialofferarchery {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-archery-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferarchery:hover { background-position: 0 100%; }

.headerspecialofferathletics {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-athletics-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferathletics:hover { background-position: 0 100%; }

.headerspecialofferbadminton {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-badminton-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferbadminton:hover { background-position: 0 100%; }

.headerspecialofferboating {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-boating-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferboating:hover { background-position: 0 100%; }

.headerspecialofferbodybuilding {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-bodybuilding-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferbodybuilding:hover { background-position: 0 100%; }

.headerspecialofferbowling {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-bowling-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferbowling:hover { background-position: 0 100%; }

.headerspecialofferboxing {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-boxing-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferboxing:hover { background-position: 0 100%; }

.headerspecialoffercanoeing {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-canoeing-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffercanoeing:hover { background-position: 0 100%; }

.headerspecialoffercricket {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-cricket-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffercricket:hover { background-position: 0 100%; }

.headerspecialoffercycling {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-cycling-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffercycling:hover { background-position: 0 100%; }

.headerspecialofferfootball {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-football-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferfootball:hover { background-position: 0 100%; }

.headerspecialoffergolf {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-golf-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffergolf:hover { background-position: 0 100%; }

.headerspecialoffergym {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-gym-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffergym:hover { background-position: 0 100%; }

.headerspecialofferhockey {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-hockey-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferhockey:hover { background-position: 0 100%; }

.headerspecialoffermotorsport {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-motorsport-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffermotorsport:hover { background-position: 0 100%; }

.headerspecialofferrowing {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-rowing-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferrowing:hover { background-position: 0 100%; }

.headerspecialofferrugby {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-rugby-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferrugby:hover { background-position: 0 100%; }

.headerspecialofferrunning {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-running-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferrunning:hover { background-position: 0 100%; }

.headerspecialoffersailing {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-sailing-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersailing:hover { background-position: 0 100%; }

.headerspecialoffershooting {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-shooting-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffershooting:hover { background-position: 0 100%; }

.headerspecialofferpool {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-pool-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferpool:hover { background-position: 0 100%; }

.headerspecialoffersocial {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-social-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersocial:hover { background-position: 0 100%; }

.headerspecialoffersports {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-sports-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersports:hover { background-position: 0 100%; }

.headerspecialoffersquash {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-squash-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersquash:hover { background-position: 0 100%; }

.headerspecialofferswimming {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-swimming-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferswimming:hover { background-position: 0 100%; }

.headerspecialoffertennis {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-tennis-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffertennis:hover { background-position: 0 100%; }

.headerspecialoffertriathlon {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-triathlon-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffertriathlon:hover { background-position: 0 100%; }

.headerspecialofferwalking {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-walking-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferwalking:hover { background-position: 0 100%; }

.headerspecialofferyachting {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-yachting-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferyachting:hover { background-position: 0 100%; }

.headerspecialofferplasticcards {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-plastic-cards-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferplasticcards:hover { background-position: 0 100%; }

.headerspecialofferevent {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-event-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferevent:hover { background-position: 0 100%; }

.headerspecialofferkey {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-key-tags-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferkey:hover { background-position: 0 100%; }

.headerspecialofferrfid {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-rfid-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferrfid:hover { background-position: 0 100%; }

.headerspecialoffercombo {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-combo-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffercombo:hover { background-position: 0 100%; }

.headerspecialofferbiodegradable {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-biodegradable-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferbiodegradable:hover { background-position: 0 100%; }

.headerspecialofferecotough {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-ecotough-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferecotough:hover { background-position: 0 100%; }

.headerspecialofferplasticfreeboard {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-plastic-free-board-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferplasticfreeboard:hover { background-position: 0 100%; }

.headerspecialofferfrosted {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-frosted-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferfrosted:hover { background-position: 0 100%; }

.headerspecialoffersatinblack {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-satin-black-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersatinblack:hover { background-position: 0 100%; }

.headerspecialoffermetallic {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-metallic-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffermetallic:hover { background-position: 0 100%; }

.headerspecialofferwhite {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-white-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialofferwhite:hover { background-position: 0 100%; }

.headerspecialoffersalonloyaltycards {
  display: block;
  width: 100%;
  aspect-ratio: 380 / 192;
  background: url("/storage/images/header-special-offer-salon-loyalty-sprite.webp") 0 0;
  background-repeat: no-repeat;
  background-size: 100% auto;
}
.headerspecialoffersalonloyaltycards:hover { background-position: 0 100%; }

/* top nav buttons — real text (was image sprites), with an animated
   underline + colour shift on hover/focus. One shared rule set since
   every product page's menu file reuses these same class names.
   Scoped under #colheadermenu: the global "a:link { color: #555 }"
   rule (class+pseudo-class specificity) otherwise beats a plain class
   selector regardless of source order — the same specificity trap
   fixed earlier for .weofferlogo img.

   flex:0 1 auto (each button sized to its own label) + white-space:
   normal, rather than shrinking font-size all the way down to stay on
   one line: per request, wording should stay as big as possible as the
   viewport narrows, wrapping onto a second line instead of shrinking
   further once a button is too narrow for its label on one line.
   Deliberately no min-width override: a flex item's default auto
   min-width is its content's min-content size, which for wrappable
   text is its single longest WORD — exactly the "never split a word"
   floor this needs, and the same technique already used for the ≤640px
   button grid below and the "Our Cards" flyout menu.

   This was flex:1 1 0% (every button an EQUAL share of the row) until
   the spacing was reported as uneven. The reason equal columns look
   uneven is that the gaps you actually see are between the WORDS, not
   between the boxes: with equal columns the box gaps are perfectly
   uniform (12px) but each label is centred in its column, so every
   button contributes half its own leftover slack to the gap on each
   side. Measured at 1385px with the old rule, the boxes were evenly
   spaced while the visible gaps ran from 12px ("Design Service &
   Artwork", which filled its column, next to the Get a Quote pill,
   which filled its own) to 56px (either side of "Order Now", a 95px
   label sitting in a 183px column). Sizing each button to its own
   label instead leaves the free space to justify-content:space-between,
   which distributes it equally BETWEEN items — re-measured at the same
   width, all six gaps came out 39-40px. */
#colheadermenu .dropdown-design-service-button,
#colheadermenu .dropdown-order-now-button,
#colheadermenu .dropdown-help-and-info-button,
#colheadermenu .dropdown-view-all-card-types-button,
#colheadermenu .dropdown-example-designs-button,
#colheadermenu .dropdown-ink-foil-colours-button,
#colheadermenu .dropdown-contact-us-enquire-button {
  display: inline-block;
  position: relative;
  flex: 0 1 auto;
  width: auto;
  aspect-ratio: unset;
  background-image: none;
  overflow: visible;
  padding: 6px 3px 8px 3px;
  font-family: "Avenir Next", Lota, sans-serif;
  font-weight: 600;
  /* a mild clamp, not the old 10-17px range — wrapping (above) now
     absorbs most of the narrowing, so this only needs to cover the
     small amount of shrink that still reads as "as big as possible"
     at the very widest and narrowest ends of this tier */
  font-size: clamp(15px, calc(0.5vw + 10px), 17px);
  line-height: 1.3;
  color: #ffffff;
  text-decoration: none;
  white-space: normal;
  text-align: center;
  transition: color 0.2s ease, transform 0.2s ease;
}

/* "Get a Quote" is the nav's primary call to action, so it's styled as
   a real filled button (solid mint pill, drop shadow) rather than the
   plain text + underline treatment the other nav items use — it needs
   to read as clickable/pressable, not just as another link. Still 20%
   larger than the shared clamp() above (each of its 3 values scaled
   ×1.2), same as before. Phone's own scaled override lives in the
   mobile section further down.
   flex:0 0 auto (not the shared 1 1 0% above) — as a pill-shaped
   button sized to its own padding+text rather than a text column, it
   doesn't want to be squeezed into an equal share with the other 6
   and wrap/distort; it just keeps its own natural size and the other
   6 share whatever space is left.
   No longer part of the shared selector list above (it needs that
   different flex value), so the rest of that list's base layout
   properties — display/position/font-family/text-decoration/
   white-space — are repeated here explicitly rather than relying on
   it; without them this fell back to browser/global-stylesheet
   defaults (a real regression caught in testing: this button rendered
   with a stray underline and the wrong font). */
/* The pill is now the .quotepillgroup WRAPPER, not the <a> itself.
   Reason: the pill holds three lines and the third ("Not sure what you
   need? Ask us") contains its own link to /enquiry/, which cannot be
   nested inside the quote <a> — nested anchors are invalid HTML and
   browsers unnest them, which would have broken the quote link. So the
   visual button treatment (mint fill, radius, shadow, hover-lift,
   press-down) moved OFF the <a> and ONTO this wrapper, and the <a>
   below is now just the top two lines of text inside it. Everything
   below is the same treatment the <a> used to carry — only the
   border-radius changed (999px → 20px: a full lozenge radius on a
   3-line block bows the short first/last lines inward and wastes
   horizontal room; 20px keeps the rounded-button read at block height).
   :focus-within (not :focus-visible) because focus now lands on one of
   two child links, never on the wrapper itself. */
/* the nav row's actual flex (>=641px) / grid (<=640px) item: stacks the
   mint pill on top of the "Ask us" line that sits under it on the blue
   bar. Its width is whichever of the two is wider - in practice the
   pill, since line 2 inside it is the longest string here. */
#colheadermenu .quotectagroup {
  display: inline-block;
  position: relative;
  flex: 0 0 auto;
  width: auto;
  text-align: center;
}

#colheadermenu .quotepillgroup {
  display: block;
  position: relative;
  width: auto;
  text-align: center;
  /* same burnt amber as the content primary buttons (a.mainbuttonquote and
     friends) — this pill is the same action, so it takes the same colour.
     It has more work to do here than they do, sitting on the navy nav bar
     rather than a pale band: amber against that navy is the highest-
     contrast pairing available from this palette, which is what makes it
     the obvious target in the header. */
  background-color: #B45309;
  border-radius: 20px;
  padding: 0.55em 1.4em;
  /* Depth + motion deliberately identical to the content pill buttons
     (a.mainbuttonquote and friends), per request — same sheen gradient,
     same base shadow, and the same hover/press below. Only the shape
     differs: this one keeps its 20px radius because it holds three
     lines, where a full 999px lozenge bows the short first and last
     lines inward. */
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.06) 0%,
    rgba(0, 0, 0, 0.08) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    0 1px 2px rgba(0, 0, 0, 0.12),
    0 3px 8px rgba(0, 0, 0, 0.12);
  transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease,
              transform 0.15s ease, filter 0.2s ease;
}
#colheadermenu .quotepillgroup:hover,
#colheadermenu .quotepillgroup:focus-within {
  /* identical to the content buttons' hover. Brightens rather than
     inverting to white — the old white-face hover was fine when the
     label was black, but with the pill green and the label white it
     would hide the text completely on hover. */
  filter: brightness(1.15);
  transform: translateY(-3px) scale(1.02);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.25),
    0 4px 8px rgba(0, 0, 0, 0.18),
    0 10px 22px rgba(0, 0, 0, 0.22);
}
/* quick, snappy press-down on click — shorter duration than the hover
   transition above so it reads as an immediate response, not a glide.
   Driven by :has(:active) on the wrapper rather than the wrapper's own
   :active so that pressing EITHER child link animates the whole pill
   (the wrapper is a <span>, so it never matches :active on its own in
   the way the old single <a> did). */
#colheadermenu .quotepillgroup:has(a:active) {
  /* identical to the content buttons' press: settles 1px onto the page
     with a shallow inner shadow, instead of the old scale(0.96) shrink
     which no longer matches anything else on the site. */
  filter: brightness(0.95);
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 3px rgba(0, 0, 0, 0.18),
    0 1px 1px rgba(0, 0, 0, 0.12);
  transition-duration: 0.05s;
}

/* the quote link itself — now a plain block of text inside the pill
   above, so every visual button property (background/radius/padding/
   shadow) is gone from here. The explicit font-family/text-decoration/
   display declarations stay: without them this falls back to the global
   `a:link` rule and renders underlined in the wrong font (a real
   regression previously caught in testing — see the notes above). */
#colheadermenu .dropdown-get-your-quote-now-button {
  display: block;
  position: relative;
  width: auto;
  aspect-ratio: unset;
  background-image: none;
  background-color: transparent;
  overflow: visible;
  font-family: "Avenir Next", Lota, sans-serif;
  text-decoration: none;
  /* white now the pill behind it is the same green as the content
     "Get a Quote" buttons (was black on pale mint) */
  color: #ffffff;
  padding: 0;
  box-shadow: none;
}

/* line 1 — the call to action. Keeps the exact clamp() the whole
   button used before (20% larger than the other 6 nav labels), so the
   CTA reads at the same size it always has; the two supporting lines
   below are sized DOWN from it via em, not up. */
#colheadermenu .quotepill-cta {
  display: block;
  white-space: normal;
  /* clamp(18px, 0.6vw + 12px, 20.4px) is exactly the other six nav
     labels' clamp(15px, 0.5vw + 10px, 17px) scaled x1.2 — the "20%
     larger than its siblings" rule this button has always been
     documented as following. It previously used a much steeper
     clamp(12px, 1.86vw - 0.6px, 20.4px) that only matched at the very
     top end: at 760px viewport that resolved to 13.5px, i.e. the
     primary CTA rendered SMALLER than the 15px plain-text links either
     side of it. Harmless when the pill was one short word; with three
     lines it made the hierarchy inside the pill collapse. */
  font-size: clamp(18px, calc(0.6vw + 12px), 20.4px);
  font-weight: 700;
  line-height: 1.2;
  color: #ffffff;
}

/* lines 2 and 3 — supporting detail, deliberately ~72% of the CTA's
   size and not bold so the pill still has one clear primary label
   rather than three competing ones. Both are sized off the SAME clamp
   as the CTA (not off a parent em) so they scale with the viewport in
   lockstep with it. */
#colheadermenu .quotepill-sub,
#colheadermenu .quotepill-ask {
  display: block;
  white-space: normal;
  font-size: clamp(13px, calc(0.43vw + 8.6px), 14.7px);
  font-weight: 400;
  line-height: 1.25;
}
#colheadermenu .quotepill-sub { margin-top: 2px; color: #ffffff; }

/* sits UNDER the pill, on #colheadermenu's blue (#1B4965) background -
   so it takes the same white the other six nav labels use, not the
   pill's black.
   font-family must be declared HERE, explicitly. Every other label in
   this header inherits "Avenir Next" from the <a> rule it lives inside
   (.quotepill-cta and .quotepill-sub are both inside the quote <a>,
   which declares it) - but this line sits OUTSIDE that <a>, and nothing
   up its ancestor chain sets a font-family, so it fell back to the
   browser's default serif (rendered as Times New Roman in testing).
   Same stack as the "Get a Quote" wording, per request. */
#colheadermenu .quotepill-ask {
  margin-top: 6px;
  color: #ffffff;
  font-family: "Avenir Next", Lota, sans-serif;
}

/* "Ask us" — underlined so it's visibly a second, separate
   destination inside a pill that is otherwise one big click target.
   Scoped under #colheadermenu (specificity 0,2,0) deliberately: the
   global `a:link, a:visited, a:active { color:#555; line-height:28px;
   text-decoration:underline; font-weight:bold; }` rule (0,1,1) has
   already beaten unscoped classes here twice before (see the two
   specificity traps in HEADER_WORK_NOTES.md) — this outranks it. */
#colheadermenu .quotepill-asklink {
  font: inherit;
  font-weight: 700;
  line-height: inherit;
  color: #ffffff;
  text-decoration: underline;
  text-underline-offset: 2px;
}
#colheadermenu .quotepill-asklink:hover,
#colheadermenu .quotepill-asklink:focus-visible {
  color: #C8E6C9;
}

/* Below 880px the quote CTA takes a full-width row of its own, ABOVE
   the six text links, instead of sitting inline with them.

   Both supporting lines are kept at every width, per request. That
   forces this: the nav is one row of 7 items and the other 6 have a
   fixed min-content floor, and at 802px (measured, stepping 2px at a
   time) that floor plus the multi-line CTA group no longer fits one
   row — it breaks into two RAGGED lines, which is the failure mode the
   ≤1360px synchronised-wrap tier exists to prevent. Since a break is
   unavoidable below that width, this makes it a deliberate one: the
   CTA gets its whole own row (order:-1 puts it first, so it reads as a
   banner CTA above the nav rather than as a link that fell off the
   end), and the six links then share the row(s) below it exactly as
   they already do. Nothing is squeezed and nothing is hidden.

   880px rather than the measured 802px: label widths here are
   font-dependent (the site asks for "Avenir Next" and falls back on
   machines without it), so the cushion has to absorb a substitution's
   metrics difference, not just rounding.

   The pill drops back to display:inline-block inside the now-full-width
   group so it still hugs its own text and sits centred, rather than
   stretching a mint bar across the whole viewport. */
@media (max-width: 880px) {
  #colheadermenu .quotectagroup {
    display: block;
    flex: 1 0 100%;
    width: 100%;
    order: -1;
    margin-bottom: 4px;
  }

  #colheadermenu .quotepillgroup {
    display: inline-block;
  }
}

/* The four ink/foil pages carry a 7th nav button, so at 881-950px —
   just after the tier above hands the Get a Quote pill back into the
   shared row — that row is trying to hold 8 items and wraps to a
   second line, which the nav must never do (per request). Measured,
   not guessed: those pages wrap at 881px but fit again by 950px, so
   they keep the CTA on its own row over exactly that band and rejoin
   the shared row above it, same as every other page. */
@media (min-width: 881px) and (max-width: 950px) {
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .quotectagroup {
    display: block;
    flex: 1 0 100%;
    width: 100%;
    order: -1;
    margin-bottom: 4px;
  }

  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .quotepillgroup {
    display: inline-block;
  }
}

.dropdown-design-service-button::after            { content: "Design Service & Artwork \25BE"; }
.dropdown-order-now-button::after                 { content: "Order Now"; }
.dropdown-help-and-info-button::after             { content: "Help & Info \25BE"; }
.dropdown-view-all-card-types-button::after        { content: "Our Cards \25BE"; }
.dropdown-example-designs-button::after           { content: "Card Examples"; }
.dropdown-ink-foil-colours-button::after          { content: "Ink / Foil Colours"; }
.dropdown-contact-us-enquire-button::after        { content: "Contact us / Enquire"; }

/* Between roughly 1120px and the ≤900px tier further down, the 6
   flex:1 1 0% equal-share buttons above wrapped independently rather
   than together: a long-labelled button's own min-content floor
   (never shrinks narrower than its longest unbreakable word) claims
   more than its nominal equal share, squeezing shorter-labelled
   siblings into wrapping first while the long one stays single-line —
   different buttons went from 1 to 2 lines at different viewport
   widths instead of all at once, per request. Forcing the exact same
   line-break points used by the ≤640px tier further down (reused
   verbatim, not redesigned) at a single shared breakpoint instead
   makes all 6 remaining text buttons switch together. ("Get a Quote"
   is no longer in this list — it's now a three-line pill whose lines
   are real markup, not a single ::after label with a forced \A break,
   so there is nothing here for it to synchronise with.)
   Threshold re-measured after the Get-a-Quote pill was widened to hold
   three lines: the wider pill leaves less room for the other six, so
   "Contact us / Enquire" — still the pairing that needs it widest —
   now first requires a second line at 1355px rather than the 1120px
   measured when the pill was a single short word. Raised to 1360px (a
   5px cushion above the measured crossover). Above that every label
   still fits on one line, so there's nothing to synchronise yet. */
@media (max-width: 1360px) {
  #colheadermenu .dropdown-design-service-button,
  #colheadermenu .dropdown-order-now-button,
  #colheadermenu .dropdown-help-and-info-button,
  #colheadermenu .dropdown-view-all-card-types-button,
  #colheadermenu .dropdown-example-designs-button,
  #colheadermenu .dropdown-ink-foil-colours-button,
  #colheadermenu .dropdown-contact-us-enquire-button {
    white-space: pre-line;
    text-align: center;
  }
  .dropdown-design-service-button::after            { content: "Design Service\A & Artwork \25BE"; }
  .dropdown-order-now-button::after                 { content: "Order\A Now"; }
  .dropdown-help-and-info-button::after             { content: "Help &\A Info \25BE"; }
  .dropdown-view-all-card-types-button::after       { content: "Our\A Cards \25BE"; }
  .dropdown-example-designs-button::after           { content: "Card\A Examples"; }
  .dropdown-ink-foil-colours-button::after          { content: "Ink / Foil\A Colours"; }
  .dropdown-contact-us-enquire-button::after        { content: "Contact\00A0us\A / Enquire"; }
}

/* Above 1360px every label is allowed back onto a single line (the tier
   above stops applying), which is fine for the usual 6 buttons but not
   for the four ink/foil pages: 7 single-line labels plus the Get a
   Quote pill no longer fit, and the row wrapped to two lines at 1361px
   and beyond (measured). The canvas is capped at 1400px wide, so there
   is no wider viewport where they would come back onto one line —
   these pages simply keep the two-line labels at every width above the
   tier, rather than having a threshold of their own.
   Scoped to min-width:1361px on purpose: the ≤640px tier further down
   sets its OWN, more aggressive 3-line breaks for these same labels,
   and those are plain (0,1,0) selectors that this (0,2,0) rule would
   otherwise outrank at phone widths. */
@media (min-width: 1361px) {
  /* #colheadermenu prefix required on the white-space rule for the same
     specificity reason as the font clamp in the phone tier: the base
     "white-space: normal" is set by "#colheadermenu .dropdown-..."
     (1,1,0). Without the ID this rule is (0,2,0) and loses — the \A
     characters below still land in the content, but a collapsing
     white-space value renders each one as an ordinary space, so the
     labels stayed single-line and the row still wrapped (measured
     exactly that, twice). */
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-design-service-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-order-now-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-help-and-info-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-view-all-card-types-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-example-designs-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-ink-foil-colours-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-contact-us-enquire-button {
    white-space: pre-line;
    text-align: center;
  }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-design-service-button::after      { content: "Design Service\A & Artwork \25BE"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-order-now-button::after           { content: "Order\A Now"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-help-and-info-button::after       { content: "Help &\A Info \25BE"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-view-all-card-types-button::after { content: "Our\A Cards \25BE"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-example-designs-button::after     { content: "Card\A Examples"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-ink-foil-colours-button::after    { content: "Ink / Foil\A Colours"; }
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-contact-us-enquire-button::after  { content: "Contact\00A0us\A / Enquire"; }
}

/* animated underline: grows from the centre outward on hover/focus.
   "Get a Quote" is excluded — it's styled as a real filled button
   below, which uses its own hover/active treatment instead. */
.dropdown-design-service-button::before,
.dropdown-order-now-button::before,
.dropdown-help-and-info-button::before,
.dropdown-view-all-card-types-button::before,
.dropdown-example-designs-button::before,
.dropdown-ink-foil-colours-button::before,
.dropdown-contact-us-enquire-button::before {
  content: "";
  position: absolute;
  left: 2px;
  right: 2px;
  bottom: 2px;
  height: 2px;
  background: #C8E6C9;
  border-radius: 2px;
  transform: scaleX(0);
  transition: transform 0.25s ease;
}

#colheadermenu .dropdown-design-service-button:hover,
#colheadermenu .dropdown-order-now-button:hover,
#colheadermenu .dropdown-help-and-info-button:hover,
#colheadermenu .dropdown-view-all-card-types-button:hover,
#colheadermenu .dropdown-example-designs-button:hover,
#colheadermenu .dropdown-ink-foil-colours-button:hover,
#colheadermenu .dropdown-contact-us-enquire-button:hover,
#colheadermenu .dropdown-design-service-button:focus-visible,
#colheadermenu .dropdown-order-now-button:focus-visible,
#colheadermenu .dropdown-help-and-info-button:focus-visible,
#colheadermenu .dropdown-view-all-card-types-button:focus-visible,
#colheadermenu .dropdown-example-designs-button:focus-visible,
#colheadermenu .dropdown-ink-foil-colours-button:focus-visible,
#colheadermenu .dropdown-contact-us-enquire-button:focus-visible {
  color: #C8E6C9;
}
.dropdown-design-service-button:hover::before,
.dropdown-order-now-button:hover::before,
.dropdown-help-and-info-button:hover::before,
.dropdown-view-all-card-types-button:hover::before,
.dropdown-example-designs-button:hover::before,
.dropdown-ink-foil-colours-button:hover::before,
.dropdown-contact-us-enquire-button:hover::before,
.dropdown-design-service-button:focus-visible::before,
.dropdown-order-now-button:focus-visible::before,
.dropdown-help-and-info-button:focus-visible::before,
.dropdown-view-all-card-types-button:focus-visible::before,
.dropdown-example-designs-button:focus-visible::before,
.dropdown-ink-foil-colours-button:focus-visible::before,
.dropdown-contact-us-enquire-button:focus-visible::before {
  transform: scaleX(1);
}

/* "Our Cards" hover dropdown */
.menu { background: transparent; border-radius: 15px; float: none; }
.menu ul { list-style: none; margin: 0; padding-left: 0; position: relative; z-index: 5050; }
.menu ul li { list-style: none; position: relative; display: block; }
.menu ul li ul { display: none; }
.menu ul li:hover ul,
.menu ul li:focus-within > ul,
.menu ul li.touch-open > ul,
.menu ul li ul:hover {
  display: block;
  position: absolute;
  margin-top: 0;
  margin-left: 0;
  /* the header navy — same value as #colheader/#colheadermenu, so the
     flyout reads as an extension of the bar it drops out of rather than a
     panel pasted over it; the box-shadow below is what lifts it off the
     page, so it does not need a colour of its own to separate itself.
     Was #5DA5C9, a leftover from the old palette (it is the embedded-form
     band's blue, not a header colour) — lighter than the bar above it, and
     white text on it managed only 2.9:1. On this navy the white items are
     9.6:1 and the mint #C8E6C9 hover/underline 7.1:1. */
  background: #1B4965;
  border-radius: 15px;
  /* Shrink-to-fit: each panel is exactly as wide as its own longest item
     and no wider, so the three menus size themselves independently
     ("Design Service & Artwork" has three short labels and no longer gets
     the same slab of empty navy as the 16-item "Our Cards" list).
     Replaces min-width: clamp(220px, 30vw, 360px), which forced every
     panel to a shared fluid width whatever it contained.
     The ceiling is a backstop for a runaway label rather than a size the
     current menus reach — the longest item today ("Clear Frosted Plastic
     Cards (Hot Foil Printed)") needs ~370px at the 18px desktop item size,
     so 400px keeps every existing item on one line while stopping a much
     longer future label from stretching the panel across the page. The
     viewport term keeps it inside the screen at tablet widths, and the
     ≤640px block below narrows that cap further for phones. */
  width: max-content;
  min-width: 0;
  max-width: min(400px, calc(100vw - 40px));
  padding: 10px 2px 10px 0;
  box-shadow: 14px 14px 45px 4px rgba(0, 0, 0, 0.65);
  z-index: 6000;
}

/* "Help & Info" opens under its own button like the others, per request.
   It used to be right-anchored (left:auto; right:0) because every panel was
   forced to a shared ~360px width and one that wide, opening rightward from
   a button that sits well into the right half of the row, ran off the
   viewport. The panels are shrink-to-fit now — this one is ~232px — so it
   clears the right edge on its own at every width from 641px up (checked at
   the narrow end, where the nav row is at its tightest relative to the
   canvas). Right-anchoring it now would only push it visibly left OF its
   own button, which is the thing being fixed.
   Below 641px the tap handler in header-mobile.php still clamps any panel
   that would overrun, so the phone case cannot regress. */
.menu li:has(> .dropdown-help-and-info-button) > ul {
  left: 0;
  right: auto;
}

.menu ul li a {
  color: #ffffff;
  text-decoration: none;
  /* em-based, not a flat 20px — scales with the fluid font-size below
     so the text/box proportions stay consistent as it shrinks */
  padding: 0 1.1em;
  /* mirrors the top-nav buttons' own fluid clamp so the dropdown's
     text shrinks in step with the "Our Cards" trigger button and the
     rest of the nav, instead of staying a fixed 18px at every width;
     capped at 18px (today's original fixed size) rather than growing
     past it on very wide screens */
  font-size: clamp(11px, calc(1.95vw - 0.65px), 18px);
  display: flow-root;
  text-align: left;
  /* unitless so it scales with font-size automatically (~2.2x, same
     ratio as the original fixed 40px/18px pair) */
  line-height: 2.2;
  font-weight: 600;
  font-family: "Avenir Next", Lota, sans-serif;
}
.menu ul li a .navtext {
  position: relative;
  display: inline-block;
}
.menu ul li a .navtext::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: #C8E6C9;
  border-radius: 2px;
  transform: scaleX(0);
  transition: transform 0.25s ease;
}
.menu ul li a:hover,
.menu ul li a:focus-visible {
  color: #C8E6C9;
}
.menu ul li a:hover .navtext::before,
.menu ul li a:focus-visible .navtext::before {
  transform: scaleX(1);
}
.menu a { text-decoration: none; }


/* --------------------------------------------------------------------------
   5. Phone header & slide-out navigation (mobile chrome, shown at ≤640px)
   -------------------------------------------------------------------------- */

#colheadermobile {
  background: url(/storage/images/header-background-mobile.webp);
  background-size: cover;
  width: 100%;
  /* was a flat 190px, sized for the old flag+logo layout — left a
     58-76px dead gap below the email banner (the tallest content) at
     320-375px once the flag was replaced by the special-offer badge.
     overflow:hidden above already gives this box a block-formatting
     context that hugs its tallest floated child's real height on its
     own, so this floor only needs to guard against a degenerate case
     (no content at all), not dictate the everyday height. */
  min-height: 80px;
  position: relative;
  margin: 0;
  overflow: hidden;
}

#colmenubuttons {
  width: 100%;
  aspect-ratio: 19 / 3;
  margin: 0;
  background-color: #1B4965;
  border-bottom: 6px solid #123A52;
}

/* #navicon / #main-nav / .nav-folder-* / .close-btn / .fade removed:
   the mobile slide-out drawer no longer exists — the desktop nav row
   + "Our Cards" dropdown stays visible at every width instead (see
   the #colheadermenu / #colmenubuttons override in section 2 and the
   phone-width clamp() continuation in section 9). */


/* --------------------------------------------------------------------------
   6. Content layout — columns, headings, buttons, components
   -------------------------------------------------------------------------- */

.col0 {
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: 20px 30px;
  float: left;
  text-align: justify;
}

.col0left  { width: 36%; box-sizing: border-box; margin: 0; padding: 0; float: left; text-align: justify; }
.col0right { width: 60%; box-sizing: border-box; margin: 0; padding: 30px 10px 0 0; float: right; text-align: justify; }

/* a floated image inline within .col0 body text (article-style intro images) —
   caps width so wrapped text has room, un-floats on phone so it doesn't
   squeeze paragraph text into a sliver */
.col0 img.float-intro { max-width: 45%; height: auto; }
@media (max-width: 640px) {
  .col0 img.float-intro { float: none !important; display: block; max-width: 80%; margin: 0 auto 20px auto !important; }
}

/* legal/policy page body text (cookies, privacy-policy, terms-and-conditions, etc.) */
.terms {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 30px;
  text-align: justify;
  font-size: 18px;
  font-family: Helvetica, sans-serif;
  font-weight: 300;
  color: #777777;
  line-height: 1.55;
}
.terms h2 .hover { text-decoration: none; font-size: 18px; font-family: Helvetica, sans-serif; font-weight: 300; color: #00C800; }
.terms a { text-decoration: none; color: #000000; font-weight: 600; line-height: 1; }
.terms strong { text-decoration: none; font-size: 18px; font-family: Helvetica, sans-serif; font-weight: 600; color: #777777; }
.terms a:hover { text-decoration: none; color: #00C800; font-weight: 600; line-height: 1; }
span.termslink { text-decoration: none; font-size: 18px; font-family: Helvetica, sans-serif; font-weight: 500; color: #777777; }
span.termslink a:hover { text-decoration: none; font-size: 18px; font-family: Helvetica, sans-serif; font-weight: 300; color: #00C800; }

/* customer-testimonials body text (same shape as .terms, different font) */
.pretty {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 30px;
  text-align: justify;
  font-size: 18px;
  font-family: Avenir Next;
  font-weight: 500;
  color: #777777;
  line-height: 1.55;
}
.pretty h2 .hover { text-decoration: none; font-size: 18px; font-family: Avenir Next; font-weight: 500; color: #00C800; }
.pretty a { text-decoration: none; color: #000000; font-weight: bold; line-height: 1; }
.pretty strong { text-decoration: none; font-size: 18px; font-family: Avenir Next; font-weight: bold; color: #777777; }
.pretty a:hover { text-decoration: none; color: #00C800; font-weight: bold; line-height: 1; }

/* yellow attention callout (cmyk-plastic-cards) */
.heading-yellow {
  font-weight: 700;
  font-size: 22px;
  line-height: 1.5em;
  font-style: normal;
  padding: 10px 25px;
  margin: 10px 0 25px 0;
  background-color: #ffff00;
  color: #ff0000;
  border: 2px solid #FF0000;
  border-radius: 25px;
  box-shadow: 5px 5px 5px #888888;
  text-align: center;
}

/* generic bold sub-heading (plastic-cards-photoseals) */
.bigheading { font-weight: 700; font-size: 24px; line-height: 1.5em; font-style: normal; color: #000000; }
.tabcol0 { width: 100%; box-sizing: border-box; }

/* fluid 16:9 video embed wrapper (YouTube iframes etc.) */
.responsive-video {
  position: relative;
  width: 100%;
  padding-bottom: 56.25%;
  height: 0;
  overflow: hidden;
}
.responsive-video iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ink/foil colour swatch grid (the four "-inks" pages) — fixed column
   count (not a fixed tile size), so the grid always fills 100% of its
   container width at any viewport size, with swatch size and gap
   both scaling fluidly to fit */
.inkswatchgrid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(8px, 1.2vw, 14px);
  float: none;
}
.inkswatchgrid .inkswatch img { width: 100%; height: auto; display: block; border-radius: 4px; }

@media (max-width: 640px) {
  .inkswatchgrid { grid-template-columns: repeat(2, 1fr); gap: clamp(8px, 3vw, 10px); }
}

.col0wording {
  width: 58%;
  box-sizing: border-box;
  margin: 20px 0 20px 30px;
  padding: 0;
  float: left;
  text-align: justify;
}

.col0buttons {
  width: 33%;
  box-sizing: border-box;
  margin: 20px 30px 20px 0;
  padding: 0;
  float: right;
  /* flex + gap, not margins on the buttons. At desktop this column is
     narrow and the buttons stack; as the page narrows the column goes
     full width (see the ≤900px rule) and they end up side by side —
     where, as plain centred inline-blocks, the only thing between them
     was the ~5px of collapsed whitespace from the markup, i.e. visually
     touching. Margins can't fix it: every one of these buttons carries
     an inline style="margin: … 0px …" in the page HTML, with zero
     horizontal margin, and an inline style outranks any stylesheet
     rule. `gap` is not a margin, so it applies cleanly without needing
     !important or edits to 40-odd page templates.
     Row gap deliberately 0 — the inline styles already provide the
     vertical spacing, and adding to it would push the stacked desktop
     pair further apart than it is today.
     text-align:center is kept for any non-flex descendant text. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* baseline, not center: these buttons carry per-page inline margins
     that are not symmetric between them (the quote button typically
     has margin-top:30px, the enquiry button 0), and centring aligns the
     MARGIN boxes — which left the two sitting 15px out of level
     whenever they shared a row (measured at 800-900px). Baseline aligns
     the label text instead, so identical buttons line up no matter what
     vertical margins the page happens to put on them. */
  align-items: baseline;
  gap: 0 18px;
  text-align: center;
}

/* Never let these two exceed the column they sit in. They set
   min-width:300px under box-sizing:content-box, so with ~57px of em-based
   side padding the real box is ~357px — wider than this column between
   roughly 887px and 989px of usable width, where it pushed the whole
   page into a horizontal scrollbar (measured: 34px of page overflow at
   887px usable). max-width alone cannot fix that, because min-width wins
   over max-width in the cascade.
   Switching to border-box and restating the floor as 300px + the 3em of
   horizontal padding reproduces today's rendered width exactly wherever
   there is room for it — including as the em padding tracks the
   font-size clamp — while allowing it to shrink to the column when
   there isn't. */
.col0buttons a.mainbuttonquote,
.col0buttons a.mainbuttonenquiry {
  box-sizing: border-box;
  /* 280px of content rather than the base rule's 300px, so the pair
     still fits side by side once the 18px gap above is taken out of the
     row. Without that 20px of give the gap alone was enough to tip them
     onto separate rows at ~800px — where they previously sat together —
     and sharing a row there is the behaviour to keep; only the touching
     was wrong. They are flex items, so where the row is roomy they
     still render wider than this floor (353px at an 800px viewport,
     against the 357px they were before), and where it is tight they
     shrink to the column instead of overflowing it. */
  min-width: min(calc(280px + 3em), 100%);
  max-width: 100%;
}

.col1 {
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 50px 0;
  padding: 0 0 0 30px;
  float: left;
  text-align: justify;
}

/* wraps every embedded Formsite form on the site (82 pages — quotation,
   order, enquiry, sample pack). Bleeds its blue to the true viewport edge
   with the same mechanism as .headingfullwidth/.weoffercontainer/.slideshow:
   the box spans 100vw with a negative margin-left, then symmetric left/right
   padding adds the gutter width back so the form itself still sits inside
   the 1400px canvas rather than stretching with the band.
   Below 1400px the gutter term resolves to 0, so band and form are the same
   width and nothing changes at those sizes. */
.colformfullwidth {
  position: relative;
  width: var(--vw, 100vw);
  box-sizing: border-box;
  /* bottom margin was 60px, which showed as a white horizontal strip between
     the blue form band and whatever full-bleed band follows it — on the
     product pages that is the dark-grey .slideshow, and the two bands butt
     together properly now. On the 82 quotation/order/enquiry pages the form
     is the last thing on the page, so this only tightens the run-in to the
     footer; the page's own trailing space still separates them. */
  margin: 0 0 0 calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 0;
  /* the form's Confirm button sits hard against the bottom of the embedded
     iframe, so with the margin gone it ended up touching the footer (or the
     slideshow band on product pages). 40px of the band's OWN blue gives it
     room without reintroducing a white strip — same trade the wording boxes
     make with their bottom padding. */
  padding-bottom: 40px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  border: none;
  float: left;
  text-align: center;
  /* MUST stay in step with `.form_table { background: … }` in all four of
     assets/form-css-quotes.css / -enquiry.css / -order-form.css /
     -sample-pack.css. Those style the form INSIDE the Formsite iframe
     (Formsite loads them from cpcards.co.uk/assets/), so this band and the
     form paint the same colour side by side — any mismatch shows up as a
     hard vertical seam at the iframe's left and right edges.
     Note this is NOT the site's brand blue #1B4965 used elsewhere in this
     file — the forms were deliberately moved off it, so don't "correct"
     one to match the other. */
  background-color: #5DA5C9;
}

/* The embedded form itself is 1200px wide — that is Formsite's own form
   width, set in the form, and it is left-aligned inside whatever box it is
   given. The embed code asks for width:100%, so the iframe was stretching
   to the full 1400px canvas and the form sat against its left edge with a
   ~200px empty strip down the right, which read as the whole band being
   off-centre.
   Sizing the IFRAME to the form's own width and centring it fixes that
   without reaching inside the frame (its content is on formsite.com, so it
   cannot be restyled from here beyond the four form-css files Formsite
   loads). !important is needed on BOTH the width and the side margins:
   EmbedManager writes style="border:0;margin:0;padding:0;width:100%"
   inline on the iframe it creates, and an inline margin:0 beats a
   stylesheet's auto margins, so without it the iframe narrowed to 1200px
   but stayed pinned to the left of the canvas.
   Nothing shows where the iframe no longer reaches: the band behind it is
   the same #5DA5C9 as the form's own background — the pairing the note
   above exists to protect — so the blue simply continues.
   max-width keeps it responsive: below ~1200px the iframe shrinks with the
   page exactly as it did before. */
.colformfullwidth iframe {
  display: block;
  width: 1200px !important;
  max-width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
}

.coldesktopfullwidth {
  width: 100%;
  margin: 0;
  padding: 0;
}
.coldesktopfullwidth img { display: block; }

/* Special-offer banner graphics: bleed a Soft Mint band off both edges, with
   the graphic itself still sitting on the 1400px canvas. 26 banners across
   10 pages (index,
   plastic-business-cards, membership-cards, loyalty-cards, gift-cards,
   salon-loyalty-cards and the four *-special-offer landing pages).

   Targeted with :has() rather than a new wrapper class because the markup
   around these graphics is not consistent — six pages have a bare
   <picture>, four wrap it in .coldesktopfullwidth > a — so a class would
   have meant editing all ten files in two different shapes. Every banner
   IS a <picture> holding an image named special-offer*, which is the one
   thing they all share. :has() is already relied on elsewhere in this file
   (see .gallery > *:has(> .headingfullwidth)); where it is unsupported the
   rule is simply dropped and the banners render as they did before.

   Two consequences of that selector worth knowing: the small header badge
   is untouched because it is not a <picture>, and a NEW banner picks this
   up automatically only if its filename keeps the special-offer prefix.

   display:block matters — <picture> is inline by default, and so is the
   <img> on the six bare-markup pages, which would leave a few pixels of
   blue under the graphic from the inline baseline gap.

   padding-top/bottom are 0 by request: the band is flush to the artwork top
   and bottom on EVERY offer, so no strip of blue shows above or below it and
   the graphic meets whatever sits either side of it directly. Only the
   left/right gutter padding remains, which is what keeps the graphic itself
   on the 1400px canvas while the blue bleeds to the viewport edge. */
picture:has(> img[src*="special-offer"]) {
  display: block;
  position: relative;
  width: var(--vw, 100vw);
  box-sizing: border-box;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 0px;
  padding-bottom: 0px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  /* Soft Mint (.softmint-background / span.softmint, defined near the top of
     this file) — by request, for every offer band on the site. Was #5DA5C9,
     the same blue as the embedded-form band .colformfullwidth; these two no
     longer need to match, so changing that blue no longer affects this. */
  background-color: #C8E6C9;
}

/* !important is needed, not lazy: four of the ten pages carry an inline
   style="... margin: 20px 0px 0px 0px" on the banner image, and an inline
   style outranks any selector. Without this those four would show 20px
   of blue above the graphic that the other six don't have — the band is
   flush top and bottom everywhere (see above), so that inline nudge is
   exactly what has to go. */
picture:has(> img[src*="special-offer"]) img {
  display: block;
  margin: 0 !important;
}

/* .offerband-flush (homepage 250-card band) no longer needs a rule of its
   own — it existed to drop that one band's 20px bottom padding back when
   the shared rule above had 20px top and bottom. Both are 0 for every
   offer now, so the class is inert and kept only because it is in the
   markup. */

.colmobilefullwidth { width: 100%; margin: 0; }

/* bleeds its background to the true viewport edge (past #canvasWrapper's
   1400px cap) while keeping its actual text aligned with the canvas: the
   box itself spans 100vw, and left/right padding adds back the exact
   gutter width — calc((100vw - min(100vw,1400px))/2) — on top of the
   original 30px, so the heading text lands in the same place it always
   did. The 1400px here must match #canvasWrapper's own max-width.
   margin-left/-right are !important because every real heading also
   carries a .headingNN font-size utility class (e.g. .heading38) whose
   combined selector also sets margin-left/-right:0 — same specificity,
   later in source, so it silently wins without !important here. */
.headingfullwidth,
.homeheadingfullwidth {
  width: var(--vw, 100vw);
  box-sizing: border-box;
  height: auto;
  margin-top: 0;
  margin-bottom: 0;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2)) !important;
  margin-right: calc(50% - calc(var(--vw, 100vw) / 2)) !important;
  padding-top: 30px;
  padding-bottom: 30px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  background-color: #9DCEF9;
  color: #37474F;
  clear: both;
}

/* Customers Include client-logos strip (homepage) */
.col020 {
  max-width: 1200px;
  margin: 20px auto;
  /* clear:both is what makes this box behave normally. The sections above
     are float:left (.weoffercontainer) and without clearing, this box's top
     started up alongside those floats — ~6600px above its own image — so its
     margin and padding landed behind the floated sections and did nothing on
     screen. Only the image's line box was pushed below the floats, which is
     why the strip appeared to sit flush against the section above it.
     With the float cleared, the box starts below them and its own spacing
     applies: the 20px margin above (previously dormant) plus the 30px
     padding added by request = 50px of clear space above the logos. */
  clear: both;
  padding-top: 30px;
  text-align: center;
}
.col020 img { width: 100%; height: auto; }

/* headingNN sizes (desktop values; phone overrides in section 9) */
.heading10, .heading12, .heading14, .heading16, .heading18, .heading20,
.heading22, .heading24, .heading26, .heading28, .heading30, .heading32,
.heading34, .heading36, .heading38, .heading40, .heading42, .heading44,
.heading46, .heading48, .heading50 {
  font-weight: 700;
  line-height: 1.5em;
  margin: 0;
}
/* Every class named in the shared block above needs a font-size of its own —
   that block only sets weight/line-height/margin. .heading26 (60 uses),
   .heading30 (2) and .heading32 (1) were listed there but had no size rule,
   so those headings fell through to the bare h2/h3 element sizes (16px/12px)
   and rendered smaller than the body copy underneath them. The unused
   10/12/16/40/50 steps are filled in at the same time so the scale is
   complete and the next one used doesn't silently break the same way. */
.heading10 { font-size: 10px; }
.heading12 { font-size: 12px; }
.heading14 { font-size: 14px; }
.heading16 { font-size: 16px; }
.heading18 { font-size: 18px; }
.heading20 { font-size: 20px; }
.heading22 { font-size: 22px; }
.heading24 { font-size: 24px; }
.heading26 { font-size: 26px; }
.heading28 { font-size: 28px; }
.heading30 { font-size: 30px; }
.heading32 { font-size: 32px; }
.heading34 { font-size: 34px; }
.heading36 { font-size: 36px; }
.heading38 { font-size: 38px; }
.heading40 { font-size: 40px; line-height: 100%; }
.heading42 { font-size: 42px; line-height: 100%; }
.heading44 { font-size: 44px; line-height: 100%; }
.heading46 { font-size: 46px; line-height: 100%; }
.heading48 { font-size: 48px; line-height: 100%; }
.heading50 { font-size: 50px; line-height: 100%; }
.heading52 { font-size: 52px; line-height: 100%; }

/* photo gallery grid (club examples, membership gallery). Flex sizing
   belongs on `.gallery`'s direct children (`<a>` for a bare linked
   thumbnail, `<div>` for a heading + linked thumbnail) — it was
   previously set on the nested `img` instead, which isn't a flex item
   at all when it's wrapped in one of those, so `flex-grow` never did
   anything and rows sat at a fixed 350px per image with unused space
   on either side, never actually reaching the 1400px canvas width. */
.gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
  justify-content: center;
  float: left;
  width: 100%;
  /* .gallery itself has no ancestor padding to lean on (it sits directly
     under #contentWrapper, not inside a padded .col0), so without this
     the now-fixed flex-grow (see the note above) makes images butt
     straight up against the canvas/viewport edge with zero breathing
     room at every width. box-sizing keeps width:100% measured inside
     this padding rather than adding to it. */
  padding: 0 20px;
  box-sizing: border-box;
}
.gallery > div,
.gallery > a {
  flex: 1 1 350px;
  max-width: 500px;
  min-width: 280px;
}

/* A gallery child holding a section heading ("Builders / Construction /
   Contractors" and friends) is a full-width divider ROW, not a card, so
   it must not take the card sizing above.

   Why this matters beyond appearance: those headings use the site's
   full-bleed trick (width:var(--vw) with margin-left:calc(50% - var(--vw)/2)),
   and that maths only lands correctly when the element's PARENT is itself
   horizontally centred in the viewport — the 50% is 50% of the parent.
   Sized as a card, the wrapper is a ~428px flex item sitting wherever the
   row happens to put it, so the heading bled to the wrong place entirely:
   measured at 1400px it rendered at x -458..927 instead of 0..1385, and
   dragged the whole page 458px wide — a huge horizontal scrollbar on
   every example page. It only looked right below ~640px, where the
   wrapper happened to be the full width of the row and therefore centred.

   Making the wrapper a full-width row fixes both symptoms at once: it
   separates the sections as intended, and because a full-width row IS
   centred in the viewport, the existing bleed maths now resolves
   correctly instead of needing to be unpicked.

   :has() is the only way to select a parent by its content; it is already
   relied on elsewhere in this file. On a browser without it these pages
   simply keep today's behaviour rather than breaking further. */
.gallery > *:has(> .headingfullwidth) {
  flex: 1 0 100%;
  max-width: none;
  min-width: 0;
  /* Each of these wrappers carries an inline style="margin: 30px 0 -30px"
     in the page markup. That -30px was tuned for the old broken layout,
     where the heading was a card-sized item; as a real full-width row it
     now fights the gallery's own gap and leaves the divider with no
     separation at all below it — exactly cancelling the 30px desktop gap,
     and going 15px NEGATIVE against the 15px mobile gap, so the heading
     literally overlapped the first card of its own section (measured at
     375px: heading bottom 1403 against card top 1388).
     Clearing just the bottom keeps the extra breathing space the inline
     style intends above each divider, and lets the gallery gap provide
     the space below. !important is required only because the margin is
     inline, and the alternative is editing it out of every example page. */
  margin-bottom: 0 !important;
}
.gallery img {
  width: 100%;
  height: auto;
  border-radius: 8px;
  object-fit: cover;
  display: block;
}

/* labelled thumbnail tile grid (card-type example picker) */
.colthumba {
  float: left;
  width: 20%;
  box-sizing: border-box;
  padding: 0 10px;
  text-align: center;
  margin-bottom: 20px !important;
}
.colthumba img { width: 100%; height: auto; border-radius: 4px; }

/* per-image example gallery thumbnail (pages whose examples aren't wrapped in a shared .gallery container) */
.colthumbexamples {
  float: left;
  width: 50%;
  box-sizing: border-box;
  padding: 0 10px;
  margin-bottom: 20px;
  text-align: center;
}
.colthumbexamples img { width: 100%; height: auto; border-radius: 8px; }

/* Real buttons — replaces this whole family of CSS-sprite background
   images (button text baked into a .webp, swapped via
   background-position on hover) with actual text + a solid fill,
   matching the "Get a Quote" nav button's own treatment: rounded pill,
   hover lift + colour invert, a quick press-down on click. Real text
   also means these are now selectable/readable to screen readers and
   search engines, and don't need an image request to render.
   Every one of these classes is used as an empty `<a></a>` (or
   `<a><span class="visually-hidden">…</span></a>`-free markup) across
   ~80-100 page templates, exactly like the old sprite — since this is
   a CSS-only change, none of those call sites need editing.
   Two colour families, matching the sprites' own original split:
   mint (primary actions — quote/enquire/order/email) and cyan
   (secondary — learn more/see examples/downloads). Text content is
   transcribed directly from each sprite image, not guessed from the
   class name (a few — e.g. .downloadfreeartworkguide, which actually
   reads "Design Service Guide" — don't match their own class name). */
a.mainbuttonquote,
a.mainbuttonenquiry,
a.mainbuttonlearnmore,
a.mainbuttonspecialoffers,
a.mainbuttonordernow,
a.mainbuttonseeexamples,
a.mainbuttonseeallexamples,
a.emailusbutton,
a.downloadfreeartworkguide,
a.downloadhighresolutionguide,
a.downloadcmykcolourguide,
a.downloadpdfguide,
a.downloadtemplate,
a.downloadexample {
  display: inline-block;
  position: relative;
  /* min-width (not a flat width) rather than max-width alone: makes
     short labels ("Get Quote", "Order now", "Email us" — natural
     widths as low as 134-185px, measured across the pages these
     appear on) stretch to match the longer ones they commonly sit
     next to in a row/grid (up to ~293px, e.g. "Download Artwork
     Guide"), so a group reads as one consistent row of buttons
     instead of a jagged mix of sizes — while still letting a
     genuinely long label ("See Examples of All Card Types", 344px)
     grow past it rather than being forced to wrap awkwardly. 300px
     was picked as just past that 293px longest "normal" label, so
     every other button lands on the exact same rendered width. */
  min-width: 300px;
  max-width: 90%;
  overflow: visible;
  text-decoration: none;
  font-family: "Avenir Next", Lota, sans-serif;
  /* Larger than the original 19px, but only semi-bold. A first pass at
     "bigger and bolder" used 800 with a heavy extruded 3D treatment and
     it read as gaudy, with the weight actually hurting legibility
     rather than helping — very heavy letterforms in white on a mid-tone
     fill lose their counters and start to blur together. 600 at a
     larger size is more readable than 800 at a smaller one, and looks
     considerably calmer. */
  font-weight: 600;
  font-size: clamp(18px, calc(1.3vw + 13px), 23px);
  letter-spacing: 0.01em;
  line-height: 1.3;
  text-align: center;
  border-radius: 999px;
  padding: 0.7em 1.6em;

  /* Restrained depth rather than a moulded plastic button. The earlier
     version stacked a strong sheen gradient, two insets, a 5px solid
     extruded side and a deep cast shadow; together they shouted. What's
     left is the smallest set that still lifts the button off the page:
     a barely-there top-to-bottom gradient (6% white to 8% black — enough
     to round the face, not enough to notice as a gradient), a 1px inner
     top highlight, and a soft two-stage shadow. No text-shadow: on a
     solid fill it only muddies the label, which was part of the
     legibility complaint.
     The `background-image` also still does the job the original
     `background-image: none` did — overriding the old sprite. */
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.06) 0%,
    rgba(0, 0, 0, 0.08) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    0 1px 2px rgba(0, 0, 0, 0.12),
    0 3px 8px rgba(0, 0, 0, 0.12);
  transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease,
              transform 0.15s ease, filter 0.2s ease;
}

/* Primary actions (quote / enquire / order / email) — mid teal.
   Two moves away from the old pale mint #C8E6C9: the label is now white
   per request, and white on that mint measures 1.34:1, i.e. effectively
   invisible. A first pass used a deep green (#2E7D32) which cleared
   contrast but sat outside the site's palette entirely and read as
   off-brand. This teal is the same hue family as the header strip and
   nav, so the buttons now belong to the page rather than sitting on top
   of it — and white lands at 5.10:1, past the WCAG AA 4.5:1 mark for
   normal-size text. Slightly brighter than the secondary family below,
   which is what gives the two their hierarchy. */
a.mainbuttonquote,
a.mainbuttonenquiry,
a.mainbuttonordernow,
a.emailusbutton {
  /* Burnt amber for the money actions, replacing the green (#148743) —
     the green never settled against the blues and, once the heading bars
     stopped being green too, it was the only warm-free odd colour left.

     Amber is the complement of the site's blues, so it is the strongest
     thing on the page WITHOUT competing with them: nothing else here is
     warm, and the navy/sky/mint everything else is built from pushes it
     forward rather than fighting it.
     This shade is as bright as white text allows — 5.0:1, past the 4.5:1
     AA line. The livelier oranges do not survive contrast: #D97706 is
     2.9:1 and #F97316 2.6:1, both of which would need dark labels and
     would then read as warning colours rather than buttons. */
  background-color: #B45309;
  color: #ffffff;
}

/* Secondary actions (learn more / see examples / downloads) — the site's
   own deep navy #123A52, already used for the nav underline and the
   .weofferheading title bars, so this is an existing palette colour
   rather than a new one. (The header contact strip used to share it too,
   but now runs a deeper #032C2C to stand apart from the navy bands
   either side of it — see #colaboveheader.) Deeper than the primary family above, so it reads as the
   quieter of the two, and white sits at 10.1:1. (A mid-brightness cyan
   was tried here first and could not stay: white on it was 2.81:1,
   failing even the relaxed 3:1 large-text threshold. The header colour
   that eventually replaced that cyan, the navy #1B4965, carries white
   fine at 9.6:1 — it is simply a different, darker role from this
   button family.) */
a.mainbuttonlearnmore,
a.mainbuttonspecialoffers,
a.mainbuttonseeexamples,
a.mainbuttonseeallexamples,
a.downloadfreeartworkguide,
a.downloadhighresolutionguide,
a.downloadcmykcolourguide,
a.downloadpdfguide,
a.downloadtemplate,
a.downloadexample {
  /* The header navy, so these read as part of the page's chrome rather
     than as a second accent competing with the amber above. That is what
     preserves the hierarchy now that the primary buttons are warm: one
     warm colour on the page means "act", everything else is structure.
     Was #1479B4, a mid blue that sat between the navy chrome and the
     sky-blue bands and belonged to neither once the palette moved.
     White on it is 9.6:1. */
  background-color: #1B4965;
  color: #ffffff;
}

a.mainbuttonquote::after            { content: "Get a Quote"; }
a.mainbuttonenquiry::after          { content: "Enquire Now"; }
a.mainbuttonordernow::after         { content: "Order now"; }
a.emailusbutton::after              { content: "Email us"; }
a.mainbuttonlearnmore::after        { content: "Learn More"; }
a.mainbuttonspecialoffers::after    { content: "See All Offers"; }
a.mainbuttonseeexamples::after      { content: "See Examples"; }
a.mainbuttonseeallexamples::after   { content: "See Examples of All Card Types"; }
a.downloadfreeartworkguide::after   { content: "Design Service Guide"; }
a.downloadhighresolutionguide::after{ content: "High Resolution Guide"; }
a.downloadcmykcolourguide::after    { content: "CMYK Colour Guide"; }
a.downloadpdfguide::after           { content: "Download Artwork Guide"; }
a.downloadtemplate::after           { content: "Download Templates"; }
a.downloadexample::after            { content: "Download Example"; }

a.mainbuttonquote:hover, .mainbuttonquote:focus-visible,
a.mainbuttonenquiry:hover, .mainbuttonenquiry:focus-visible,
a.mainbuttonlearnmore:hover, .mainbuttonlearnmore:focus-visible,
a.mainbuttonspecialoffers:hover, .mainbuttonspecialoffers:focus-visible,
a.mainbuttonordernow:hover, .mainbuttonordernow:focus-visible,
a.mainbuttonseeexamples:hover, .mainbuttonseeexamples:focus-visible,
a.mainbuttonseeallexamples:hover, .mainbuttonseeallexamples:focus-visible,
a.emailusbutton:hover, .emailusbutton:focus-visible,
a.downloadfreeartworkguide:hover, .downloadfreeartworkguide:focus-visible,
a.downloadhighresolutionguide:hover, .downloadhighresolutionguide:focus-visible,
a.downloadcmykcolourguide:hover, .downloadcmykcolourguide:focus-visible,
a.downloadpdfguide:hover, .downloadpdfguide:focus-visible,
a.downloadtemplate:hover, .downloadtemplate:focus-visible,
a.downloadexample:hover, .downloadexample:focus-visible {
  /* Was a full colour invert to a white face with teal text — a hard
     flip that fought the button's own identity mid-interaction. Now it
     stays the same object and just lifts a little: a touch brighter,
     1px up, shadow softened outward. `filter: brightness()` handles the
     lightening so a single rule serves both colour families without a
     hover colour per family. Deliberately understated — the earlier
     3px lift with a deep shadow was part of what read as gaudy. */
  text-decoration: none;
  /* More pronounced than the first pass, per request: it lifts 3px and
     grows 2% instead of nudging 1px, brightens harder, and casts a
     deeper shadow — so the button clearly reacts without going back to
     the heavy extruded look that read as gaudy. */
  filter: brightness(1.15);
  transform: translateY(-3px) scale(1.02);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.25),
    0 4px 8px rgba(0, 0, 0, 0.18),
    0 10px 22px rgba(0, 0, 0, 0.22);
}

/* quick, snappy press-down on click — shorter duration than the hover
   transition above so it reads as an immediate response, not a glide */
a.mainbuttonquote:active,
a.mainbuttonenquiry:active,
a.mainbuttonlearnmore:active,
a.mainbuttonspecialoffers:active,
a.mainbuttonordernow:active,
a.mainbuttonseeexamples:active,
a.mainbuttonseeallexamples:active,
a.emailusbutton:active,
a.downloadfreeartworkguide:active,
a.downloadhighresolutionguide:active,
a.downloadcmykcolourguide:active,
a.downloadpdfguide:active,
a.downloadtemplate:active,
a.downloadexample:active {
  /* Settles onto the page: drops the 1px the hover raised it, loses the
     outer shadow and picks up a shallow inner one. Small, quick and
     physical — the previous 4px drop was too theatrical for the calmer
     button this now is. */
  text-decoration: none;
  filter: brightness(0.95);
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 3px rgba(0, 0, 0, 0.18),
    0 1px 1px rgba(0, 0, 0, 0.12);
  transition-duration: 0.05s;
}

/* a row of CTA buttons that wraps and centres on smaller screens */
.buttonrow {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: center;
  align-items: center;
  padding: 10px 30px 30px 30px;
  clear: both;
}

/* A button row sitting directly under a special-offer graphic clears it by
   exactly 40px, per request — on every page that pairs the two, not just
   the homepage.

   Both markup shapes are matched because the offer banners are wrapped
   inconsistently (the same reason the banner rule itself uses :has()):
     - bare <picture> followed by the row — index.php and the four
       *-special-offer landing pages;
     - <div class="coldesktopfullwidth"><a><picture>…</a></div> followed by
       the row — gift-cards, loyalty-cards, membership-cards,
       plastic-business-cards, salon-loyalty-cards.
   The adjacent-sibling combinator is what keeps this off the other
   .buttonrow instances on those same pages (13 pages use the class, most
   of them nowhere near an offer).

   40px lands as a true 40px of clear space: the band has no bottom padding
   and the graphic is display:block, so nothing sits between the artwork's
   bottom edge and this padding.
   Left/right/bottom padding still come from .buttonrow above. This
   supersedes the old homepage-only .buttonrow-offer250 { padding-top:90px },
   which is why that class now has no rule of its own at this tier. */
picture:has(> img[src*="special-offer"]) + .buttonrow,
.coldesktopfullwidth:has(img[src*="special-offer"]) + .buttonrow {
  padding-top: 40px;
}

/* spec thumbnails grid (unchanged from live, already flex) */
.grid-container {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  margin: 30px 0 30px 30px;
}
.grid-row { display: flex; gap: 30px; justify-content: flex-start; flex-wrap: wrap; }
.grid-row-centre { justify-content: center; }
.grid-row a { flex: 0 0 calc(20% - 30px); display: block; line-height: 0 !important; text-decoration: none !important; }
.grid-row img { width: 100%; height: auto; display: block; }
.grid-container a.fancybox,
.grid-row a.fancybox {
  display: block;
  line-height: 0 !important;
  text-decoration: none !important;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  font-size: 0 !important;
}

/* price table (unchanged from live) */
.price-section { font-family: Arial, Helvetica, sans-serif; padding: 2rem 1.5rem 0 1.5rem; max-width: 600px; }
.price-heading { font-size: 22px; font-weight: 700; color: #333; margin: 0 0 6px; }
.price-sub { font-size: 14px; color: #666; margin: 0 0 20px; }
.price-table { width: 100%; border-collapse: collapse; font-size: 15px; }
.price-table thead tr { background: #1B4965; color: #fff; }
.price-table thead th { padding: 12px 16px; text-align: left; font-weight: 600; font-size: 14px; }
.price-table thead th:last-child { text-align: right; }
.price-table tbody tr { border-bottom: 1px solid #e8e8e8; }
.price-table tbody tr:last-child { border-bottom: none; }
.price-table tbody tr:hover { background: #fdf5f6; }
.price-table td { padding: 11px 16px; color: #333; }
.price-table td:last-child { text-align: right; font-weight: 600; color: #1B4965; }
.qty-badge { display: inline-block; background: #f5f5f5; border-radius: 4px; padding: 2px 10px; font-size: 14px; font-weight: 700; color: #333; min-width: 40px; text-align: center; }
.popular-row td { background: #fff8f8; }
.popular-badge { display: inline-block; background: #1B4965; color: #fff; font-size: 11px; padding: 2px 8px; border-radius: 3px; margin-left: 8px; font-weight: 600; vertical-align: middle; }
/* line-height is the fix for "the 2nd line is always too far from the
   1st". This is a 12px note, but it inherits the global
   `p { line-height: 28px }` — a FIXED 28px, sized for 20px body copy.
   On 12px text that is a 2.3x line-height, so the <br /> between the two
   sentences opened a gap more than twice the height of the text itself.
   Setting a unitless value makes it scale with this element's own font
   instead of the body's. margin-top trimmed at the same time, since the
   note should read as belonging to the table it annotates. */
.vat-note { font-size: 12px; color: #888; margin-top: 6px; line-height: 1.45; }
.vat-note span { color: #1B4965; font-weight: 600; }

/* The "Minimum order 25 cards — … Standard turnaround 7 working days …"
   line under the How-to-Order steps (9 pages). It needs ~773px to sit on
   one line but .price-section caps its children at 600px, so it always
   broke onto two — hence this opt-in class rather than a change to
   .vat-note itself, which would also hit the price-table notes on
   biodegradable/key-tags/combo-cards/etc. Those carry a deliberate <br />,
   belong to the table above them, and must stay narrow.

   width:max-content lets the line size to its own text and overflow the
   600px cap (nothing between here and .col0 clips, and .col0 is the full
   1400px canvas, so there is room). max-width then measures against the
   PAGE rather than the 600px parent, so the line still wraps normally as
   the window narrows — one line on desktop, two or more below ~860px.
   The 84px is .col0's 30px side padding (x2) plus .price-section's own
   24px left padding. Below 640px those paddings shrink to 20px/16px, so
   the real figure there is 72px — the note is wrapping at those widths
   regardless, so the 12px difference only shifts the wrap point slightly
   and isn't worth a second breakpoint.
   var(--vw) not 100vw, for the same scrollbar reason as the bleed rules. */
.vat-note.order-note {
  width: max-content;
  max-width: calc(min(var(--vw, 100vw), 1400px) - 84px);
}

/* slideshow */
/* bleeds a dark-grey band to the true viewport edge either side of the
   slider, same mechanism as .headingfullwidth/.weoffercontainer above:
   box spans 100vw with a negative margin-left, then left/right padding
   adds back the gutter width so the slider itself still sits inside the
   1400px canvas. Below 1400px the gutter term is 0, so the band and the
   slider are the same width and nothing changes visually.
   The old `margin-bottom: -60px` existed only to cancel flexslider.css's
   own `margin: 0 0 60px` on .flexslider. That cancellation is now done at
   the source (.slideshow .flexslider below) instead — with a background
   on this box the negative margin would have let the next section paint
   over the bottom 60px of the band, which reads as the grey stopping
   short on one edge. Net vertical spacing is unchanged either way. */
.slideshow {
  position: relative;
  width: var(--vw, 100vw);
  box-sizing: border-box;
  margin: 0 0 0 calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 0;
  padding-bottom: 0;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  border: none;
  float: left;
  text-align: justify;
  background-color: #242424;
}

/* flexslider.css loads after this file, but `.slideshow .flexslider` is
   the more specific selector so these win on specificity, not order.
   background:transparent so the plugin's default white sheet (and the
   white it would show through its own 4px border-radius corners) doesn't
   sit as a pale halo on top of the new dark band. */
.slideshow .flexslider {
  margin-bottom: 0;
  background: transparent;
}

/* "we offer" boxes — single responsive layout replacing the old fixed-height
   desktop boxes AND the image-based mobile boxes */
/* bleeds its softmint-/softskyblue-background to the true viewport edge the
   same way .headingfullwidth does above (see its comment for the mechanism)
   — box spans 100vw, left/right padding adds back the gutter width so the
   flex children (icon/heading/wording) land at the same effective width
   and position as before, since percentage/flex-basis sizing on those
   children resolves against this container's padding-adjusted content box. */
.weoffercontainer {
  position: relative;
  width: var(--vw, 100vw);
  box-sizing: border-box;
  float: left;
  margin-top: 0;
  margin-right: 0;
  /* was margin-bottom:60px + padding-bottom:20px. The 60px was WHITE space
     between the bottom of a coloured box and the next heading bar, which
     made the gap read as belonging to neither. That separation now lives
     entirely inside the box so it is coloured: the box runs straight into
     the next heading with no white strip, and the generous coloured tail
     under the wording is what shows at a glance which wording goes with
     which heading.
     Started at 80px (which just preserved the old 20+60 spacing), then
     raised to 120px by request, then set from the other end: the target is
     now a measured 90px of colour below the LAST thing in the box, per
     request. That is not one padding value, because what sits last differs
     — a "Learn More" button box carries its own 10px bottom padding, so
     80 + 10 = 90; a box with no button ends in the wording (or the icon
     beside it), which the rule below handles. Phone tier keeps the same
     idea at a smaller figure — see the ≤640px override. */
  margin-bottom: 0;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 0;
  padding-bottom: 80px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2);
  text-align: left;
  /* background colour comes from a paired .softmint-background /
     .softskyblue-background modifier class — not set here, so source
     order between this rule and those utility classes never matters */
  /* was overflow:hidden — removed so its own h2/h3.weofferheading child
     (the dark-teal title bar) can bleed to the viewport edge in turn; see
     that rule's comment. Tested for regressions (logo-bleed-30/-15 icon
     images inside these boxes) across gift-cards/hotel-key-cards/etc —
     none found. */
  overflow: visible;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
}

/* Boxes with no "Learn More" button end in the wording — or, where the icon
   beside it is the taller of the two (e.g. the homepage's "100% Recyclable"
   box), in the icon. Both are given the same 90px of colour beneath them as
   a button gets:
     - the wording already carries a 30px bottom margin, so 60 + 30 = 90;
     - the icon column has none, so it is given a matching 30px here, scoped
       to these boxes only. In a box WITH a button the icon is not the last
       thing in the box, and adding a margin there would push the button row
       down by 30px on every page that has one, which is why this is not on
       .weofferlogo itself.
   Written as :not(:has(…)) rather than a class because the markup has no
   flag for "this box has a button" — the button block is simply absent. */
.weoffercontainer:not(:has(.weofferfindoutmore)) { padding-bottom: 60px; }
.weoffercontainer:not(:has(.weofferfindoutmore)) .weofferlogo { margin-bottom: 30px; }
/* same 90px for the plain-text variant of these boxes (the homepage intro
   sits in one). Here the trailing space is split three ways — 60px from the
   container above, 20px here, and the last paragraph's own 10px bottom
   margin — so this is 20px rather than the wording's 30px. */
.weoffercontainer:not(:has(.weofferfindoutmore)) > .col0 { padding-bottom: 20px; }

/* bleeds its own dark-teal background to the viewport edge, same
   mechanism as .headingfullwidth/.weoffercontainer above — otherwise
   this title bar only spans the canvas width while its parent
   .weoffercontainer's lighter mint/sky background now bleeds past it,
   leaving a visible seam either side. Needs .weoffercontainer's own
   overflow:hidden removed (done above) since it's the nearer clipping
   ancestor, not #canvasWrapper. */
h2.weofferheading, h3.weofferheading, h2.weofferheadingsmaller, h3.weofferheadingsmaller {
  /* flex-basis must match width exactly (100vw, not the old 100%) —
     when flex-basis isn't auto, flexbox uses IT instead of the width
     property for main-axis sizing, so leaving this at 100% silently
     ignored the width:100vw below and rendered a mismatched size. */
  flex: 1 1 var(--vw, 100vw);
  width: var(--vw, 100vw);
  box-sizing: border-box;
  height: auto;
  margin-top: 0;
  margin-bottom: 0;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  margin-right: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 30px;
  padding-bottom: 30px;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  /* was left — centred with the rest of the site's headings. The left/right
     padding above is symmetric (gutter + 30px each side), so the text
     centres on the canvas rather than on the viewport, which is what keeps
     it lined up with the centred heading bars above and below it. */
  text-align: center;
  font-weight: 700;
  font-size: 44px;
  font-family: Lota, sans-serif;
  color: #FFFFFF;
  line-height: 1em;
  /* Deep navy, replacing the dark green-teal these bars used to carry.
     Once the header/nav moved to #1B4965 the green was the one colour on
     the page that belonged to no other element, and it read as a leftover.
     This is the same hue as the header navy, two steps darker, so the bars
     sit in the same family as the chrome above them while staying clearly
     the darker, heavier element on the page. White on it is 12:1.
     Set here via the shared palette value (span.darkteal /
     .darkteal-background near the top of this file), so the nav underline
     and the Google-reviews accents moved with it and nothing is left
     green. */
  background-color: #123A52;
  position: static;
}

/* Icon column beside the wording (Get A Quote, Design Service £20, Don't
   Panic, Free Delivery box …).

   The column sits INSIDE the 1400px canvas at every width — a flat 30px in
   from the canvas edge, which is where .weoffercontainer's own gutter padding
   already puts it. Its band still bleeds to the true viewport edge, but no
   image is ever laid out out in that bleed area: past 1400px the page stops
   widening and so does everything in it, per request.

   (It did previously reclaim that gutter with a negative left margin —
   width calc(250px + gutter), margin-left calc(30px - gutter) — so the icons
   centred in the empty band colour to their left. That is what let them drift
   out past the canvas edge: 27px at 1585px, ~130px at 1920px.)

   The wording is unaffected by the change: the column's outer width was
   (30px - gutter) + (250px + gutter) and is now 30px + 250px — 280px either
   way. Only the icon itself moves, back inside the canvas. Below 1400px the
   gutter is 0 and the two are identical anyway, so nothing changes there. */
.weofferlogo {
  order: 2;
  flex: 0 0 auto;
  width: 250px;
  height: auto;
  padding: 0;
  margin: 30px 0 0 30px;
  position: static;
  align-self: center;
}

/* Fluid icon size, up from the flat 250px — smaller as the window narrows,
   and never wider than its column (max-width:100%), which is what caps it at
   250px from 1400px up (the column no longer grows past the canvas edge — see
   above) and at the tablet/phone figures set in those tiers.

   margin-left/right:auto are !important because ~264 of these <img> tags
   carry a raw inline margin from the original markup with an ad-hoc
   horizontal nudge — 0, +40, +25, +10, -10, -15, -20, -30 across
   different pages — and an inline style outranks any selector. Those
   nudges are precisely the inconsistent alignment being fixed here, so
   they are overridden rather than preserved. Only the horizontal pair is
   forced: each image's inline top/bottom margin still applies, so the
   deliberate vertical nudges are left alone. */
.weofferlogo img {
  width: clamp(200px, 17vw, 310px);
  max-width: 100%;
  height: auto;
  display: block;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* logos that bleed off the left edge of their box (from live inline styles) */
.weofferlogo img.logo-bleed-30 { margin: -10px 0 0 -30px; }
.weofferlogo img.logo-bleed-15 { margin: -10px 0 0 -15px; }

/* The "Free Photorealistic Email Proof" hand-holding-a-card icon (54 pages) is
   a deliberate exception to the centring above: the arm is cropped at the
   artwork's own left edge, so it only reads correctly sitting hard against the
   page edge, where it looks like it continues off-screen. Centred like the
   other icons it just looks like a floating cut-off hand.

   Matched on src rather than a class because the markup is inconsistent: 15
   pages use class="logo-bleed-30", 38 carry the equivalent as a raw inline
   margin, and 1 has only a margin-top. The src is the one thing all 54 share.

   Nothing else moves horizontally beyond its own column: the image shifts
   inside the column and the column itself is widened (below) to make room for
   the larger artwork, so only the wording beside it gives ground. Tablet and
   phone tiers override this below, where the column sits at a different inset.

   POSITION — flush to the 1400px canvas edge, NOT the true viewport edge: past
   1400px the page stops widening, so the hand stops with it and stays lined up
   with the left edge of everything else on the page instead of drifting out
   into the bleed band. Below 1400px there is no gutter and the canvas edge and
   the viewport edge are the same thing, so the artwork still runs off the side
   of the screen there.
   The column's own left edge is a flat 30px in from the canvas edge at every
   width (see .weofferlogo above), so a flat -30px lands the image exactly on
   that edge — no gutter term needed anywhere in this pair of rules.

   SIZE — a flat 370px, up from the previous 290px by request, and constant at
   every desktop width because the slot it sits in is constant: the wording
   starts at 30 (column inset) + 330 (column width) + 30 (its own margin) from
   the canvas edge, and the image starts at that edge, so 370px leaves a 20px
   gap to the wording whatever the screen size.
   max-width:none is required: the inherited max-width:100% would otherwise
   clamp this back to the column's own width. */
.weofferlogo:has(img[src*="logo-realistic-email-proof"]) {
  /* wider than the shared 250px icon column purely to make room for the
     enlarged artwork — the wording beside it gives up 80px, negligible
     against its ~1000px measure, and no other icon is affected because this
     only matches the column holding this one image. */
  width: 330px;
}

#contentWrapper .weofferlogo img[src*="logo-realistic-email-proof"] {
  width: 370px;
  max-width: none;
  margin-left: -30px !important;
  margin-right: 0 !important;
}

.weofferwordingbase, div.weofferwording {
  order: 3;
  flex: 1 1 400px;
  width: auto;
  height: auto;
  padding: 0;
  border: none;
  margin: 30px;
  position: static;
  z-index: 30;
  text-align: left;
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5em;
  color: #000000;
}

/* Base copy size, matched to the rest of the site's body text (the 20px
   `p` set in section 1, e.g. the homepage's "CP Cards is a UK plastic card
   printing company…" intro) — per request, so copy reads at one size
   throughout rather than jumping to a larger setting inside these
   coloured blocks. Was 26px here, which made these sections — by far the
   most common home for body copy on the site — noticeably bigger than
   the same kind of wording elsewhere on the same page.
   The deliberately-large lines are NOT scaled down with it: .weofferwording32
   /34/54 below keep their sizes, so against 20px copy they now stand out
   more than they did against 26px (1.6x / 1.7x / 2.7x, up from 1.2x / 1.3x
   / 2.1x) while staying weight 700. */
.weofferwording p {
  font-weight: 300;
  font-size: 20px;
  font-family: Lota, sans-serif;
  line-height: 1.4em;
  color: #555555;
}

/* Inline emphasis inside that copy ("...within 10 minutes", "free"). It
   matches the sentence it sits in and carries its weight, not its size —
   at the old 26px it was the same size as the surrounding paragraph, so
   holding it at 26px now would make emphasised words larger than the
   sentence around them. line-height follows the paragraph's 1.4em for the
   same reason (it was a fixed 38px, sized for 26px text). */
.weofferwording a,
.weofferwording span {
  font-weight: bold;
  font-size: 20px;
  font-family: Lota, sans-serif;
  line-height: 1.4em;
  color: #555555;
}

span.weofferwording32 { font-weight: 700; font-size: 32px; line-height: 1.5em; }
span.weofferwording34 { font-weight: 700; font-size: 34px; line-height: 1.5em; }
span.weofferwording54 { font-weight: 700; font-size: 54px; line-height: 1.5em; }

.weofferfindoutmore {
  order: 4;
  flex: 1 1 100%;
  width: auto;
  height: auto;
  /* top 30px, not 10px: the button sits 60px below the end of the wording,
     per request. The wording's own 30px bottom margin supplies half of
     that, this padding the other half — they are separate flex rows, so
     the two add rather than collapse.
     (Where the icon column is taller than the wording, the button row
     starts below the ICON instead and the measured gap from the wording is
     naturally larger — 60px is the spacing after whichever of the two runs
     lower, which is what keeps the button clear of both.)
     The 10px bottom stays: it is the other half of the 90px of colour
     under the button (80px container padding + 10px here). */
  padding: 30px 30px 10px 30px;
  border: none;
  position: static;
  display: flex;
  /* centred at every width, per request — was flex-end, i.e. the button
     sat hard against the right edge of its panel on desktop and only
     centred once the ≤900px override kicked in, so it appeared to jump
     as the page narrowed. This is by far the most common home for these
     buttons (717 of them), so it's the rule that decides whether "the
     buttons are centred" is true site-wide.
     The other three containers already centre: .buttonrow and
     .col0buttons via their own justify-content, and the loose ones sit
     in wrappers carrying an inline text-align:center. */
  justify-content: center;
  flex-wrap: wrap;
  box-sizing: border-box;
}

/* optional-extras reveal (checkbox toggle) */
.more-info {
  max-height: 0;
  overflow: hidden;
  transition: max-height 1s ease;
  background: #f0f0f0;
  clear: both;
}
#toggle:checked ~ .more-info { max-height: 3000px; }

/* mobile-era leftovers still used by unconverted includes */
.mobilebox {
  width: 100%;
  margin: 10px 0;
  padding: 0 0 15px 0;
  float: left;
  text-align: justify;
}


/* --------------------------------------------------------------------------
   7. Footer (single footer for all widths)
   -------------------------------------------------------------------------- */

#pageFooter { width: 100%; float: left; height: auto; }

/* bleeds its charcoal background to the viewport edge, same mechanism
   as the header rules above — footer columns/email text/social+
   payment icons stay canvas-aligned via the compensated padding.
   The seal-badge background image is deliberately pinned to the
   canvas-equivalent width (min(100vw,1400px)) instead of the box's
   own full bled width, and centered within it — so the badges stay
   exactly where they were before this bleed feature (same size, same
   position relative to the email text between them) while only the
   flat charcoal colour extends into the gutters either side. */
.mainfooter {
  width: var(--vw, 100vw);
  box-sizing: border-box;
  height: auto;
  /* was 40px, which was the white strip sitting above the footer on every
     page. The sections that run into the footer all carry their own bottom
     padding now (.weoffercontainer 120px, .gr-band 55px, .colformfullwidth's
     form), so the footer can butt straight up against them without anything
     looking cramped. */
  margin-top: 0;
  margin-bottom: 0;
  margin-left: calc(50% - calc(var(--vw, 100vw) / 2));
  padding-top: 0;
  padding-bottom: 0;
  padding-left: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  padding-right: calc((var(--vw, 100vw) - min(var(--vw, 100vw), 1400px)) / 2 + 30px);
  /* the two seals that used to live in this background image
     (mainfooter-background-fullwidth.webp) are now real linked <img>
     elements in the markup — see .footertop below. Nothing replaces the
     image here; the footer is a flat colour with the seals laid out in
     flow, which is also what lets the middle block use the full space
     between them instead of having to dodge painted artwork. */
  background-color: #222222;
  float: left;
  text-align: justify;
}

/* Footer top row — "Over 30 years" seal | email + social | "Design service"
   seal. Replaces the old .mainfooter background image, which painted both
   seals and therefore made them unclickable; they are now linked images
   pointing at /about-us/ and /design-service/.

   The proportions come from that background: it was drawn 2000px wide and
   scaled to the canvas, with the left seal 16.2% of the width and the right
   one 16.85%, both starting at the same height. 17% here reproduces that at
   every width, and the max-widths cap each seal at the size it reached when
   the background stopped growing at the 1400px canvas (227px and 236px).
   Percentage widths mean they still scale down on narrow screens exactly as
   the background did. */
.footertop {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  /* the canvas less .mainfooter's own 30px side padding, so this row lines
     up with the footer columns underneath it */
  max-width: 1340px;
  margin: 0 auto;
}
.footerseal {
  flex: 0 0 auto;
  width: 17%;
  max-width: 227px;
  /* matches where the seals began in the old artwork (56/526 of its height,
     ~39px once scaled to the 1400px canvas) */
  margin-top: 40px;
}
.footerseal-design { max-width: 236px; }
.footerseal img { width: 100%; height: auto; display: block; }
.footertopcentre { flex: 1 1 auto; min-width: 0; }

/* Footer link hover — the three social icons (Facebook / YouTube /
   Instagram) and the two seals either side of them, all sharing the header
   logo's gesture (#colheadercpcardslogo a): same brighten, same 3px lift
   with a 1.02 scale, same easing, same 1px press, and the same drop-shadow
   pair rather than a box-shadow, since these are transparent artwork too.
   Two deliberate differences from the logo's copy of this:
     - transform-origin stays at the default centre. The logo anchors its
       scale to the left so it grows away from the page edge; these sit with
       space either side, so centre is right.
     - `> a` on the social wrapper, so the payment-cards image sitting in it
       (not inside a link) is untouched.
   The opacity pin overrides the .imagehover class the social <img> tags
   carry (0.8 on hover), which would otherwise dim them mid-lift. */
.footersocial > a,
.footerseal {
  display: inline-block;
  transition: transform 0.15s ease, filter 0.2s ease;
}
.footersocial > a:hover,
.footersocial > a:focus-visible,
.footerseal:hover,
.footerseal:focus-visible {
  transform: translateY(-3px) scale(1.02);
  filter: brightness(1.15)
          drop-shadow(0 4px 8px rgba(0, 0, 0, 0.18))
          drop-shadow(0 10px 22px rgba(0, 0, 0, 0.22));
}
.footersocial > a:active,
.footerseal:active {
  filter: brightness(0.95) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
  transform: translateY(1px);
}
.footersocial > a:hover img.imagehover,
.footersocial > a:focus-visible img.imagehover { opacity: 1 !important; }

.footercolumns {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
  justify-content: space-between;
  margin-top: 100px;
}

.footercolumn {
  flex: 1 1 280px;
  width: auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  float: none;
  line-height: 14px;
  text-align: left;
}

.copyright {
  width: 100%;
  margin: 20px 0 5px 0;
  padding: 0;
  float: left;
  text-align: center;
  clear: both;
}

a.footera:link, a.footera:visited { color: #ffffff; text-decoration: none; font-size: 30px !important; font-weight: 600; font-family: Lota, sans-serif; line-height: 24px; }
a.footera:hover { color: #00C800; }

a.footerb:link, a.footerb:visited { color: #f1f1f1; text-decoration: none; font-size: 15px !important; font-weight: 400; font-family: Lota, sans-serif; line-height: 20px; }
a.footerb:hover { color: #00C800; }

.mainfooter span { color: #ffffff; text-decoration: none; font-size: 14px !important; font-weight: 500; font-family: Lota, sans-serif; line-height: 28px; }
span.footer12 { color: #ffffff; text-decoration: none; font-size: 12px !important; font-weight: 400; font-family: Lota, sans-serif; line-height: 14px; }
span.footerheading { color: #ffffff; text-decoration: none; font-size: 18px !important; font-weight: 600; font-family: Lota, sans-serif; line-height: 28px; }


/* --------------------------------------------------------------------------
   8. Tablet adjustments (641px – 900px)
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {

  #colheader { min-height: 0; padding-bottom: 10px; }

  /* header shrinks with the logo at this range, so the offer's pull-up
     offset must shrink with it too (viewport-relative, not a fixed px) —
     otherwise it drifts above the header and gets clipped */
  #colheaderspecialoffer { top: calc(-10px - 20vw); }

  .col0wording  { width: 100%; margin: 20px 0; padding: 0 30px; }
  .col0buttons  { width: 100%; margin: 0 0 20px 0; padding: 0 30px; }
  /* margin-bottom is what separates the two once they stop being
     side-by-side columns and become stacked rows. As columns they never
     needed vertical spacing — side by side, the gap between them is
     horizontal — so nothing provided any, and the moment they stacked
     the price table sat flush against the example image below it
     (measured: a 0px gap at every width from 320 to 900). Scales a
     little with the viewport so the separation stays proportional
     rather than looking cramped on a phone and mean on a tablet. */
  .col0left     { width: 100%; margin-bottom: clamp(22px, 3.5vw, 38px); }
  .col0right    { width: 100%; padding: 0 30px; }

  /* This is the width at which .col0left and .col0right stop being two
     side-by-side columns and stack instead (the two rules directly
     above). Their CONTENTS were still laid out as though they were
     columns, so once stacked they sat off to one side of the page
     rather than centred — the price table and the example-card image
     being the visible case. Measured on /plastic-business-cards/ at
     785px before this rule: the price block sat at x24 with 161px of
     empty page to its right, and the card image at x105 with 30px to
     its right. Both now centre.

     Two separate causes, hence two fixes:
       - .price-section carries a fixed 600px width with zero margins,
         so in a full-width parent it just sits at the left edge. Auto
         side margins centre it. (Applied to every direct child of the
         two columns, not only that one block: any fixed-width child
         has the same problem, and auto margins are a no-op for the
         full-width ones.)
       - the example image is floated by an INLINE style="float: right"
         in the page markup — 43 pages carry it. Inline styles outrank
         any selector, so clearing it needs !important; that is the
         narrow, justified use of it rather than a shortcut. Once the
         float is off, the image is display:block with a definite width
         and auto margins centre it the same way.
     Both are scoped to this breakpoint only, so the desktop two-column
     layout is untouched. */
  .col0left > *,
  .col0right > * {
    margin-left: auto;
    margin-right: auto;
  }
  .col0left > img,
  .col0right > img,
  .col0left > a > img,
  .col0right > a > img {
    float: none !important;
    display: block;
    margin-left: auto;
    margin-right: auto;
  }

  .colthumba { width: 33.333%; }

  h2.weofferheading, h3.weofferheading, h2.weofferheadingsmaller, h3.weofferheadingsmaller { font-size: 32px; padding: 20px 25px; }
  .weofferlogo { flex: 0 0 180px; width: 180px; margin: 25px 0 0 25px; }
  /* the column's left inset is 25px in this tier, not the desktop 30px, so
     the email-proof icon's pull-back has to match it to stay on the viewport
     edge. Its column widens here too (180 -> 210) for the same reason as on
     desktop, which puts the wording at 25 + 210 + 25 = 260px, so 240px keeps
     the same 20px gap. */
  /* flex-basis as well as width: this tier sets `flex: 0 0 180px`, and a
     definite flex-basis is what sizes the item, not the width property. */
  .weofferlogo:has(img[src*="logo-realistic-email-proof"]) { flex-basis: 210px; width: 210px; }
  #contentWrapper .weofferlogo img[src*="logo-realistic-email-proof"] {
    width: 240px;
    margin-left: -25px !important;
  }
  .weofferwordingbase, div.weofferwording { margin: 25px; flex: 1 1 300px; }
  .weofferwording p { font-size: 20px; }
  .weofferwording a, .weofferwording span { font-size: 20px; line-height: 30px; }
  span.weofferwording32 { font-size: 24px; }
  span.weofferwording34 { font-size: 26px; }
  span.weofferwording54 { font-size: 40px; }

  .footercolumns { margin-top: 60px; }
}


/* --------------------------------------------------------------------------
   9. Phone (≤640px) — mobile chrome, single column, phone type sizes
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {

  /* (The phone-only centre-anchor for "Help & Info" that used to live
     here has been REMOVED — superseded by the row-anchored flyout rule
     further down in this same tier.
     Its comment claimed centring on the button's own midpoint "keeps it
     on-screen regardless of which grid column the button ends up in".
     That was simply not true, and measurably so: a flyout is ~337px
     wide at a 375px viewport, so centring it needs ~168px of clearance
     each side of the button's midpoint — which only exists near the
     middle of the screen. It happened to hold while the button sat in
     column 2 of a 3-column grid, and broke the moment the nav became a
     single 6-column row and the button moved to column 5, putting the
     flyout 65px off the right edge. Anchoring every flyout to the nav
     row instead removes the per-button geometry this depended on.) */

  /* #colaboveheader (flag/wording/heart + email) now stays visible at
     phone width too (see the .desktop-only:has() override earlier in
     this file).

     ONE row at phone width, per request: the flag / "Proudly printed
     in England" / heart cluster sits alongside the email address and
     its "We aim to reply within 10 minutes 9-5 Mon-Fri" subtext,
     rather than the two being stacked on separate lines.

     The thing that makes this possible without the subtext becoming
     unreadable is that the subtext is allowed to WRAP here (see its
     own rule further down). Sharing the row caps the email block at
     the email address's own width — ~122px at 320px, ~141px at 375px —
     and that phrase is 44 characters, so on a single line it can only
     fit by dropping to ~4.9-5.6px, which is why it was previously
     stacked instead. Wrapping it over two lines roughly halves the
     width each line needs, so the same ~122-141px budget supports a
     legible size instead.

     Watch the narrow end: brand + email block is a genuinely tight fit
     at 320px (it overflowed by 20px before the clamps below were
     retuned for it), so anything that widens either side — a longer
     brand phrase, a bigger email clamp — needs re-measuring at 320px,
     not just eyeballing at 375px. */
  #colaboveheader {
    flex-wrap: nowrap;
    /* the two sides fill the row exactly (the email block shrinks to
       take whatever the brand cluster leaves), so without an explicit
       gap the heart icon and the email text end up touching */
    column-gap: 8px;
    padding-top: 8px;
    padding-bottom: 8px;
  }
  /* 0 1 auto, i.e. allowed to SHRINK — this is what lets the email block
     give way to the brand cluster and its own contents rewrap, instead
     of the row overflowing. Deliberately no `min-width: 0` alongside it:
     the default auto minimum keeps the block from shrinking below its
     own min-content, and without that floor the (nowrap) email address
     spills out of its box leftward — right-aligned content overflowing
     to the left — and lands on top of the brand cluster. Measured that
     happening at 320-480px before the floor was restored. */
  .headeremailgroup { flex: 0 1 auto; }
  /* the safety valve for the narrowest phones. Held on one line the
     address is ~122px at 320px, and the brand cluster needs ~164px of
     the 280px available, so the two cannot both fit. Allowing it to
     wrap drops its min-content to its longest single token
     ("sales@cpcards.co.uk") and the row fits again. It only actually
     wraps where it must: measured, it takes two lines at 320px and
     stays on one from 360px up. */
  #colaboveheader .headeremailusbanner { white-space: normal; }
  .colaboveheaderbrand { gap: clamp(3px, calc(1.25vw - 1px), 8px); }
  /* all three (flag, wording, heart) scaled 20% bigger than their
     original clamp values, per request */
  .colaboveheaderbrand img {
    width: clamp(16.8px, calc(5.25vw), 33.6px) !important;
    height: auto !important;
  }
  .colaboveheaderbrand img[src*="heart"] {
    width: clamp(14.4px, calc(3vw + 4.8px), 24px) !important;
  }
  /* trimmed ~10% (was clamp(10.8px, 1.5vw + 6px, 15.6px)) to buy back
     the width the email block needs now that the two share one row
     again. Measured at 320px: this takes the brand cluster from 178px
     to 164px, which is what keeps the email address on a single line
     from 360px up rather than wrapping on every common phone size. */
  .colaboveheaderwording {
    font-size: clamp(9.7px, calc(1.35vw + 5.4px), 14px);
    white-space: nowrap;
  }
  #colaboveheader .headeremailusbanner {
    /* first attempt scaled the whole clamp by 1.25x uniformly, but
       that pushed the 320px-floor size up to ~11.2px — enough to
       close the deliberate margins from the fix above back to ~0
       (measured directly, not assumed). Kept the floor at the
       original 9px (still fits with a genuine margin at 320px) and
       grew the ceiling instead, so it's noticeably bigger everywhere
       above the very narrowest phones without reopening that fix. */
    font-size: clamp(9px, calc(2.65625vw + 0.5px), 17.5px);
    /* bottom trimmed to 1px (was 4px, matching the other 3 sides) so
       the subtext below sits closer here too, per request */
    padding: 4px 2px 1px 2px;
  }

  /* (The phone-only swap to a shorter subtext phrase that used to live
     here has been removed — the full "We aim to reply within 10 minutes
     9-5 Mon-Fri" wording is now kept at every width, per request.)

     white-space:normal is the load-bearing part, not the font-size.
     Sharing the row with the brand cluster (see #colaboveheader above)
     caps this line at the email address's own width, ~122px at 320px /
     ~141px at 375px. Held on ONE line, 44 characters in that space
     computes to ~4.9-5.6px — measured, and genuinely unreadable, which
     is what forced the earlier stacked layout. Letting it wrap over two
     lines roughly halves the width each line needs, so the same budget
     supports the sizes below instead.

     Sized deliberately smaller than the email address it sits under
     (~0.85x of that clamp across the phone range) so it still reads as
     subordinate subtext, and so the wrapped block stays visually tidy
     rather than competing with the address.

     matchEmailSubtextWidth() in jquery_functions.js must NOT fight this:
     it stands down whenever this element's computed white-space allows
     wrapping, because its whole method — measure the string once at a
     reference size, scale linearly to a target width — assumes a single
     unwrapped line and is meaningless once the text flows over two. */
  .headeremailsubtext {
    /* ALWAYS one line, per request (this briefly wrapped over two).
       The size that keeps it on one line is not something a clamp can
       express, because it depends on how much room the brand cluster
       leaves rather than on the viewport alone — matchEmailSubtextWidth()
       in jquery_functions.js measures that remaining space and sets the
       exact font-size to fill it. The clamp here is only the pre-JS
       fallback, sized low enough that there is no flash of a too-wide
       line before the JS runs. */
    white-space: nowrap;
    text-align: right;
    font-size: clamp(4.5px, calc(1.4vw + 0.2px), 11px);
  }

  /* At phone width, keeping the wording on one line forced the font
     down to ~7.5-8px to fit all 5 per row — too small to read
     comfortably. Instead, let each button's own label wrap onto two
     lines (equal-width flex columns, like the old mobile icon row's
     layout) so the font can stay a legible size instead of shrinking
     to fit a single line. */
  #colheadermenu .dropdown-design-service-button,
  #colheadermenu .dropdown-order-now-button,
  #colheadermenu .dropdown-get-your-quote-now-button,
  #colheadermenu .dropdown-help-and-info-button,
  #colheadermenu .dropdown-view-all-card-types-button,
  #colheadermenu .dropdown-example-designs-button,
  #colheadermenu .dropdown-ink-foil-colours-button,
  #colheadermenu .dropdown-contact-us-enquire-button {
    flex: 0 1 auto;
    white-space: pre-line;
    text-align: center;
    /* Sized so every nav button fits on ONE row at every width, per
       request (they used to drop to 2 rows below 640px, and to 3 on the
       four ink/foil pages that carry a 7th button). One row across 6
       columns is what dictates these endpoints — a grid track can never
       be narrower than the label's longest unbreakable WORD (see the
       .navrow rule below for why that floor is deliberately kept), so
       the font is what has to give. Endpoints measured against the
       worst-case label rather than estimated: "Examples" is the widest
       single word in the set once the line-breaks below are applied.
       The 7-button pages get their own, slightly smaller clamp under
       the :has() rule further down — the extra column has to come from
       somewhere, and shrinking all 48 pages to fit the 4 tightest ones
       would be the wrong trade. */
    font-size: clamp(10px, calc(1.5625vw + 5px), 15px);
    line-height: 1.2;
    /* horizontal padding trimmed again (3px -> 2px each side) — across
       6 buttons that is another 12px back into the row, which at the
       320px floor is most of a whole character's worth of font size. */
    padding: 4px 2px 6px 2px;
  }

  /* "Get a Quote" made visually prominent: 20% larger than the shared
     clamp() above (each value scaled ×1.2), plus the same filled-pill
     button treatment as desktop — re-applied here because the shared
     mobile rule above resets every button (including this one) back
     to flat padding/no background. */
  /* Phones keep both supporting lines too (per request). .navrow is a
     CSS grid at this width, not flex, so the "own row" treatment from
     the ≤880px tier above has to be expressed as a grid span rather
     than a flex-basis: grid-column:1/-1 makes the CTA claim every
     column, and order:-1 puts that row first. Without the span it
     would sit in a single ~60px column and the wording would either
     shrink to unreadable size or wrap to six lines.
     Type is NOT scaled down to phone sizes here (no clamp override):
     the CTA now has the full row width to itself, so the desktop
     clamp's 18px floor fits comfortably and keeps the CTA legible —
     shrinking it would be solving a space problem that no longer
     exists at this width. */
  #colheadermenu .quotectagroup {
    grid-column: 1 / -1;
    order: -1;
    width: 100%;
    margin-bottom: 2px;
  }

  #colheadermenu .quotepillgroup {
    display: inline-block;
    padding: 0.5em 1.1em;
  }

  #colheadermenu .dropdown-get-your-quote-now-button {
    font-size: clamp(15.6px, calc(2.16vw + 7.2px), 18px);
    font-weight: 700;
    /* must track the desktop rule — the pill behind it is green at every
       width now, so black here would only misfire on phones */
    color: #ffffff;
    background-color: transparent;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
  }

  /* a few labels contain a single word wider than an equal ~57-60px
     column at this font size ("Photorealistic", "Personalisation",
     "Example", "Artwork") — mid-word breaking (overflow-wrap:anywhere)
     "fixed" the overflow but split whole words across lines, which
     isn't acceptable. Instead: no forced equal-width columns (flex:0 1
     auto, default min-width so a button never shrinks below its widest
     un-splittable line) and .navrow already has flex-wrap:wrap from
     the base rule above, so a button whose longest word needs more
     room simply claims it — and if the row can't fit all 5 buttons at
     their natural width, the extra ones wrap onto their own line,
     instead of the word itself getting cut. */

  /* force a deliberate break on every button (not just the ones that
     happen to overflow) so all labels land on a consistent set of short
     lines instead of a jagged mix of 1-line and multi-line buttons —
     "\A" is a forced line break, honoured because the rule above
     switches white-space to pre-line.

     Two labels are broken more aggressively here than in the wider
     tiers above (3 lines rather than 2), because with every button now
     sharing ONE row the columns are far narrower than they used to be,
     and a track can never be narrower than the label's longest
     unbreakable token:
       - "Design Service" -> its own line each, so the widest token is
         "Service"/"Artwork" (7 chars) rather than the pair together.
       - "Contact\00A0us" -> the non-breaking space is deliberately
         DROPPED here (it is kept in the wider tiers above). It was
         added when this tier was a roomy 5-column grid, to stop
         "Contact us" wrapping to a 4-line button when there was
         plenty of width for it on one line. At 6-7 columns that
         reasoning inverts: holding "Contact us" together as one
         10-character atom makes it the widest token in the entire nav
         and would force every other column down to match it. Letting
         it break puts the button on a deterministic 3 lines and hands
         the whole row back roughly two characters of font size.
     Net effect: "Examples" (8) is now the widest token in the set, and
     it is what the font clamp above is sized against. */
  .dropdown-design-service-button::after            { content: "Design\A Service\00A0&\A Artwork\00A0\25BE"; }
  .dropdown-order-now-button::after                 { content: "Order\A Now"; }
  .dropdown-help-and-info-button::after             { content: "Help\00A0&\A Info\00A0\25BE"; }
  .dropdown-view-all-card-types-button::after       { content: "Our\A Cards\00A0\25BE"; }
  .dropdown-example-designs-button::after           { content: "Card\A Examples"; }
  .dropdown-ink-foil-colours-button::after          { content: "Ink\00A0/\A Foil\A Colours"; }
  /* NOTE the space after \00A0 below: a CSS hex escape consumes up to
     SIX hex digits, and "E" (of "Enquire") is itself a hex digit, so
     "\00A0Enquire" parses as the single codepoint U+00A0E — an
     unassigned character that renders as a tofu box and swallows the
     "E", giving a literal "?nquire" on screen (caught in testing). One
     whitespace character immediately after an escape is consumed as
     its terminator, so "\00A0 Enquire" is nbsp + "Enquire" with no
     stray space. The other \00A0 uses in this block are followed by
     "&", "/" or another escape, none of which are hex digits, so they
     don't need the terminator. */
  .dropdown-contact-us-enquire-button::after        { content: "Contact\A us\A /\00A0 Enquire"; }

  /* ONE row for every nav button, at every width, per request (this
     used to be 5 columns => 2 rows for the usual 6 buttons, and 3 rows
     on the ink/foil pages that carry a 7th).
     Deliberately no min-width:0 override — leaving each button at its
     default automatic min-width means a grid track never shrinks
     narrower than that button's own widest un-splittable line, so a
     long word grows its own column instead of overflowing it or
     getting split mid-word. That floor is why the font clamp above,
     not the track count, is what absorbs the narrowing. */
  .navrow {
    display: grid;
    /* minmax(min-content, 1fr), not a bare 1fr — a documented CSS Grid
       quirk: with a bare `fr` track (implicit `auto` minimum), an item
       that spans multiple tracks is excluded from automatic minimum
       track sizing, so a multi-column item (the Get a Quote CTA, which
       spans 1/-1 below) gets squeezed into a box far narrower than its
       own text. An explicit min-content minimum isn't subject to that
       exclusion. */
    /* auto tracks + space-between, NOT equal 1fr columns, for the same
       reason the desktop tier uses flex:0 1 auto rather than flex:1 1 0%
       — see that rule's note. Equal columns space the BOXES evenly but
       centre each label inside its own column, so the gaps a reader
       actually sees vary by however much slack each label leaves.
       Measured here at 375px with the old equal-column track list:
       visible gaps ran 9/3/12/20/12px (a 17px spread) even though the
       tracks were nominally equal. With auto tracks each column hugs
       its label and space-between hands the leftover out equally:
       re-measured at the same width, 13/13/13/13/13.
       `auto` (not max-content) is deliberate: an auto track's minimum is
       min-content, so it can still shrink toward the longest-word floor
       when space is tight instead of overflowing the row, which keeps
       the one-row-at-every-width guarantee intact at the 320px end. */
    grid-template-columns: repeat(6, auto);
    justify-content: space-between;
    gap: 10px clamp(2px, calc(0.5vw), 5px);
    align-items: flex-start;
  }

  /* The four ink/foil pages (translucent / metallic / satin-black /
     white plastic cards) carry a 7th nav button, "Ink / Foil Colours",
     that the other 44 pages don't have. Detected structurally with
     :has() rather than by adding a body class to 4 page templates —
     same technique already used for .desktop-only:has(#colaboveheader)
     near the top of this file, and the one selector here with a real
     browser floor (Safari 15.4+ / Chrome 105+; older browsers simply
     get the 6-column track list and the 7th button wraps, i.e. exactly
     today's behaviour, not a broken layout).
     The extra column has to be paid for out of the same row width, so
     these pages also take a slightly lower font clamp — sized the same
     way as the 6-column one, against "Examples" as the widest token,
     just divided seven ways instead of six. */
  .navrow:has(.dropdown-ink-foil-colours-button) {
    grid-template-columns: repeat(7, auto);
  }
  /* #colheadermenu prefix is REQUIRED, not decorative: the base clamp
     these override is written as "#colheadermenu .dropdown-..."
     (specificity 1,1,0), so a plain ".navrow:has(...) .dropdown-..."
     (0,2,0) loses to it no matter how much later it appears — the
     7-button pages silently kept the 6-button font and overflowed
     their row by 24px at 320px (measured). With the ID in front these
     are (1,2,0) and win. */
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-design-service-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-order-now-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-help-and-info-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-view-all-card-types-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-example-designs-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-ink-foil-colours-button,
  #colheadermenu .navrow:has(.dropdown-ink-foil-colours-button) .dropdown-contact-us-enquire-button {
    font-size: clamp(8.5px, calc(2.03125vw + 2px), 15px);
  }

  /* "Contact us / Enquire" wraps after "Contact us" (the "\A" below),
     but min-content sizing for wrappable text is based on its single
     longest WORD ("Contact" or "Enquire"), not the "Contact us" phrase
     — so the grid track was sized just narrow enough that "Contact us"
     itself wrapped again into "Contact"/"us" on separate lines (4
     lines total instead of 2). Tried a fixed min-width first, but that
     pins the fix to this browser's exact font-rendered pixel width —
     a font substitution (different system, no "Avenir Next" installed)
     could render "Contact us" wider and reopen the same wrap. Using a
     non-breaking space between "Contact" and "us" (in the ::after
     content below) is font-independent: the browser can never wrap
     between them regardless of how wide they render, so min-content
     sizing correctly treats "Contact us" as one atomic unit in any font. */

  /* (The old <=380px fallback to a 3-column grid has been removed. It
     existed to keep the row from overflowing at the narrow end by
     letting the buttons wrap onto more rows — the opposite of what is
     now wanted. Staying on one row down to 320px is handled instead by
     the font clamp + tightened gap/padding above, whose endpoints were
     measured against that 320px floor rather than estimated.) */

  /* Dropdown flyouts span the whole nav row on phones, anchored to the
     ROW rather than to the individual button that opened them.

     Why not anchor to the button (what this used to do): a flyout is
     ~337px wide at a 375px viewport, i.e. very nearly the full screen,
     so it only fits if its anchor button is hard against the left
     gutter. Any other column overflows. That was patched per-button —
     a left-anchor for "Our Cards", a right-anchor for "Help & Info" on
     desktop, a centre-anchor for it on phones — and each of those
     patches silently depended on where that button happened to sit in
     the row. Measured consequences of that fragility, at 375px:
       - "Design Service & Artwork" had NO phone rule at all and ran
         207px off-screen in the old 3-column layout.
       - "Help & Info" fitted only by luck of being in column 2; once
         the nav became a single 6-column row it moved to column 5 and
         its centre-anchor put it 65px off-screen.
     Anchoring to the row removes the whole class of bug: there is no
     per-button geometry left to get wrong, and it cannot regress when
     buttons are reordered or the column count changes again.

     THAT ROW-ANCHORING HAS NOW BEEN LIFTED, per request: the flyouts open
     under the button that triggered them here exactly as they do on
     desktop. What makes that safe again is the width — the panels are
     shrink-to-fit now (width:max-content, so 255px/132px/143px at 375px
     rather than the ~337px full-bleed panels the warning above was
     written about), and a panel that narrow has room to sit under its own
     button. Three things keep it from regressing:
       - max-width caps every panel at the viewport less the header's own
         20px gutters, so no amount of long item text can push one
         off-screen; it wraps inside the panel instead.
       - "Help & Info", the only dropdown button in the right half of the
        row, is right-anchored by the shared rule further up this file
        (.menu li:has(> .dropdown-help-and-info-button) > ul) — that rule
        is not width-scoped, so it applies here too and its panel opens
        leftward from the button.
       - the tap handler in header-mobile.php clamps a panel back inside
         the viewport if it would still overrun, which covers any future
         button that gains a dropdown without someone remembering to add
         a right-anchor for it.
     .navrow keeps position:relative (harmless, and it is what the clamp
     measures against); the intermediate .menu/ul are still static so the
     panel resolves against its own <li>, which IS the button. */
  .navrow { position: relative; }
  .navrow .menu,
  .navrow .menu ul { position: static; }

  .menu ul li:hover ul,
  .menu ul li:focus-within > ul,
  .menu ul li.touch-open > ul,
  .menu ul li ul:hover {
    top: 100%;
    transform: none;
    min-width: 0;
    width: max-content;
    max-width: calc(100vw - 40px);
  }

  /* Flyout item text at phone size. The shared clamp on .menu ul li a is
     tied to the nav BUTTONS' own scale — it bottoms out at 11px, which is
     right for six labels squeezed across one row but far too small for a
     tap list, and it left the "(Hot Foil Printed)" suffixes (12px) larger
     than the item text they qualify. 16px matches the body copy and every
     other link on a phone. The inherited unitless line-height of 2.2 then
     gives ~35px rows, comfortably past the 24px minimum tap target.
     Items that no longer fit the panel's max-width simply wrap onto a
     second line inside it.
     Set at 16px first, then eased back to 14px by request — still well
     clear of the 11px it was, still ~31px rows, and it keeps the longer
     card names on one line in a narrower panel. */
  .menu ul li ul a { font-size: 14px; }

  /* Moved into the mobile header bar itself, in the old "printed in
     England" flag badge's spot (top-right, alongside the logo),
     instead of appearing as its own full-width banner further down —
     the flag image was removed from header-mobile.php entirely.
     #colheadermenu's own `position:relative` (set unconditionally,
     used on desktop to anchor this same element a few hundred px
     lower down) would otherwise capture this absolute positioning
     right where it already sits — dropped to `static` below so the
     search continues up to #canvasWrapper's new positioning context,
     letting the badge escape upward into the header row instead. */
  #colheadermenu { position: static; }

  #colheaderspecialoffer {
    position: absolute;
    /* #colheadermobile is back to being the first visible element on
       mobile (the flag/wording/heart/email strip, #colaboveheader,
       was moved to render AFTER it — see the reordering note further
       down), so this badge's positioning ancestor (#canvasWrapper —
       it's a descendant of #colheadermenu, not of #colheadermobile,
       see the note above) once again coincides with #colheadermobile's
       own top. 13px was the original measured value for that case;
       nudged up a further few px per request. */
    top: 8px;
    right: 15px;
    /* shares the header row with the logo (per the logo's own cap just
       below) rather than a fixed small icon size — the offer graphic is
       a dense text banner, illegible much smaller than this. 35%/48%
       (not a naive 50/50 split) deliberately leaves a genuine ~30-39px
       gap to the logo's own right edge across the whole 320-375px
       range (measured directly, not guessed) — the first attempt at
       45%/50% left only a ~6px gap, which visibly overlapped the
       logo graphic's own baked-in tagline text at 375px. */
    width: 35%;
    max-width: 200px;
    margin: 0;
    z-index: 5250;
  }

  /* capped so it actually leaves room for the badge above instead of
     claiming the full declared 280px width (this rule beats the HTML
     width attribute, same fluid mechanism as the generic
     #colheadermobile img rule above, just with a lower ceiling at this
     breakpoint). The logo graphic itself bakes in the tagline text and
     "Over 30 Years" seal (not just a logo mark) — shrinking it further
     than necessary makes that text harder to read, so this stays as
     large as it can while still leaving the badge's required gap. */
  #colheadermobile img[alt="colour plastic cards limited logo"] {
    max-width: 48%;
  }

  body { -webkit-text-size-adjust: none; }

  p { font-size: 16px !important; line-height: 1.5em; }

  /* Body copy that is NOT inside a <p> has to come down to the same 16px.
     #contentWrapper carries the site's 20px body size (with !important),
     and the rule above only ever lowered <p> elements — so any copy written
     as bare text nodes with <br> separators kept inheriting 20px on phones
     and read noticeably larger than the paragraphs around it. The tick list
     under "Our main aims are to provide:" on /about-us/ is exactly that
     shape, and it is a common pattern in this markup, so the fix belongs on
     the wrapper rather than on that one list.
     Everything with a size of its own is unaffected: headings use the
     .headingNN scale above, links and list items have their own phone
     rules, and the weoffer/footer blocks set their own sizes. */
  #contentWrapper { font-size: 16px !important; line-height: 1.5em; }

  a:link, a:visited, a:active {
    font-family: Lota, sans-serif;
    font-size: 16px;
    line-height: 1.5em;
    font-weight: bold;
    text-decoration: underline;
    color: #555555;
  }
  a:hover { color: #00C800; }

  li { font-size: 14px; }

  /* was text-align:left !important — headings are centred at every width
     now, so this phone-tier exception would have been the one place h1
     still sat left. margin-left:5px kept for the small side inset. */
  h1 { text-align: center !important; margin-left: 5px; }

  /* Phone heading scale.
     The small end used to fall BELOW the 16px body copy — .heading14 was
     11px, .heading18 14px, .heading20 15px and .heading22 exactly 16px —
     so an <h2>/<h3> read as smaller than the paragraph under it (see
     /about-us/: "The Colour Plastic Cards Limited Mission" and "Why Choose
     Colour Plastic Cards Limited?"). Every one of these classes is used
     only on h1-h4 elements (checked across the site: 794 uses, none on a
     span or div), so there is no small-print case that wants to stay tiny.
     The scale is therefore floored at 17px and steps up from there —
     compressed at the small end the way a phone scale should be, but never
     below the copy it introduces. The large display sizes (.heading36 and
     up) are unchanged; they were already fine. */
  .heading10 { font-size: 15px; }
  .heading12 { font-size: 16px; }
  .heading14 { font-size: 17px; }
  .heading16 { font-size: 18px; }
  .heading18 { font-size: 19px; }
  .heading20 { font-size: 20px; }
  .heading22 { font-size: 21px; }
  .heading24 { font-size: 22px; }
  .heading26 { font-size: 23px; }
  .heading28 { font-size: 24px; }
  .heading30 { font-size: 24px; }
  .heading32 { font-size: 25px; }
  .heading34 { font-size: 26px; }
  .heading36 { font-size: 26px; }
  .heading38 { font-size: 27px; }
  .heading40 { font-size: 28px; }
  .heading42 { font-size: 29px; }
  .heading44 { font-size: 30px; }
  .heading46 { font-size: 31px; }
  .heading48 { font-size: 32px; }
  .heading50 { font-size: 33px; }
  .heading52 { font-size: 34px; }

  .gallery { gap: 15px; }
  .gallery > div,
  .gallery > a { flex: 1 1 100%; min-width: 0; max-width: 100%; }

  .colthumba { width: 50%; padding: 0 6px; }

  .colthumbexamples { width: 100%; float: none; padding: 0; }

  /* the page-title bar. 16px made it exactly the size of the body copy
     underneath, so the page's <h1> ("About us", "Plastic Card Printing —
     Made in England") read as no more important than a paragraph. 24px
     puts it clearly at the top of the phone scale above — it still wraps
     to two lines on the longest titles at 320px, which is fine for a
     centred band. !important is inherited from the original rule: these
     bars also carry a .headingNN class, so the size has to beat it. */
  .headingfullwidth,
  .homeheadingfullwidth {
    padding: 10px;
    font-size: 24px !important;
    text-align: center !important;
  }

  .col0,
  .col0wording,
  .col0left,
  .col0right,
  .col1 {
    width: 100%;
    padding: 10px 20px;
    margin: 0;
    text-align: left !important;
  }
  /* the shared `margin: 0` above wipes the stacked-row separation set in
     the ≤900px tier, so it has to be restated here — without this the
     price table sat flush against the example image again below 640px
     while looking correct at 800-900px */
  .col0left { margin-bottom: clamp(22px, 3.5vw, 38px); }

  /* NOT part of the text-align:left group above — this holds the
     Get Quote/Enquire Now buttons + a sample card image, which should
     stay centred at every width, not follow the body-text columns */
  .col0buttons {
    width: 100%;
    padding: 10px 20px;
    margin: 0;
    text-align: center;
  }

  /* some pages (the club-membership-cards-examples family +
     salon-loyalty-cards) hardcode style="float:right" directly on
     these buttons — float wins over the parent's text-align
     regardless of specificity, so it has to be cleared explicitly */
  .col0buttons .mainbuttonquote,
  .col0buttons .mainbuttonenquiry {
    float: none !important;
  }

  .grid-container { margin: 20px 0; }
  .grid-row { gap: 10px; justify-content: center; }
  .grid-row a { flex: 0 0 calc(30% - 0px); }

  .buttonrow { gap: 12px; padding: 10px 20px 20px 20px; }
  /* The 40px clearance above is a desktop/tablet composition decision — on
     a phone that much dead space above three stacked full-width buttons is
     a lot of scrolling, so phones keep the shared compact padding. Selectors
     repeated verbatim from that rule because a media query adds NO
     specificity: a bare .buttonrow-offer250 (0,1,0) would lose to the
     :has()-based pair (0,2,2) even down here, and phones would silently
     inherit the 40px. */
  picture:has(> img[src*="special-offer"]) + .buttonrow,
  .coldesktopfullwidth:has(img[src*="special-offer"]) + .buttonrow { padding-top: 10px; }
  /* Pill buttons go full-width on phones.

     This REPLACES a pair of rules that read
       .mainbuttonquote, .mainbuttonenquiry, ... { width: 280px; }
     and which never once took effect, for two independent reasons —
     worth spelling out, because the first is the same trap that has
     now caught this stylesheet four separate times:
       1. Specificity: written as ".mainbuttonquote" (0,1,0) against a
          base rule of "a.mainbuttonquote" (0,1,1), so the base won no
          matter the source order.
       2. Even had it won, `width` cannot override `min-width` — and
          the base rule sets `min-width: 300px`. For the same reason
          the base rule's own `max-width: 90%` is dead too: min-width
          beats max-width in the CSS cascade.
     With `box-sizing: content-box` and ~48px of horizontal padding,
     that left every one of these buttons with a hard 348px floor. The
     measured result was a horizontal scrollbar on 41 of 48 pages:
     348px of button against 280px of available width at a 320px
     viewport = 68px of page overflow, 13px at 375px, gone by 414px —
     arithmetic that matches the measurements exactly.

     The three properties below are all load-bearing: min-width:0
     releases the 300px floor, box-sizing:border-box makes the padding
     count inside the width instead of adding 48px to it, and width
     100%/max-width 100% pins the button to its container. Dropping
     any one of them reopens the overflow. */
  a.mainbuttonquote,
  a.mainbuttonenquiry,
  a.mainbuttonlearnmore,
  a.mainbuttonspecialoffers,
  a.mainbuttonordernow,
  a.mainbuttonseeexamples,
  a.mainbuttonseeallexamples,
  a.emailusbutton,
  a.downloadfreeartworkguide,
  a.downloadhighresolutionguide,
  a.downloadcmykcolourguide,
  a.downloadpdfguide,
  a.downloadtemplate,
  a.downloadexample {
    box-sizing: border-box;
    min-width: 0;
    width: 100%;
    max-width: 100%;

    /* Everything below this line is RE-ASSERTED, not new styling: it
       restates values these buttons already carry from their base rule
       further up the file, because this tier contains its own
       "a:link, a:visited, a:active { font-family: Lota; font-size:16px;
       line-height:1.5em; font-weight:bold; text-decoration:underline;
       color:#555 }" (a few hundred lines above this one, meant for body
       copy links). That rule ties the base button rule on specificity
       — both are (0,1,1) — and, sitting later in the file, wins.
       The visible effect was that on phones ONLY, all 14 pill buttons
       silently reverted to grey #555 underlined Lota text, i.e. they
       stopped looking like buttons at all, while desktop stayed
       correct — which is presumably why it went unnoticed: the styling
       these values come from was signed off on desktop.
       This block is after that rule, so re-stating the values here is
       enough; no !important and no specificity bump needed. */
    font-family: "Avenir Next", Lota, sans-serif;
    /* These four must stay in step with the base rule further up — they
       exist only to out-rank the mobile a:link rule, not to differ from
       it. If the base font-size/weight/colour changes and this is left
       behind, the buttons silently revert on phones only, which is
       exactly the failure this block was added to fix in the first
       place. Currently mirroring: 800 / clamp(17px, 1.25vw+12.4px, 22px)
       / white. */
    font-weight: 600;
    font-size: clamp(18px, calc(1.3vw + 13px), 23px);
    line-height: 1.3;
    text-decoration: none;
    color: #ffffff;
  }

  /* we-offer boxes become single column.
     Same change as the desktop rule: the 25px was a white gap before the
     next heading, so it moves inside the box as colour instead. 70px, not
     the desktop 120px — the desktop figure is a lot of empty colour on a
     375px screen, and this keeps the same proportion to it that the old
     phone spacing had to the old desktop spacing.
     margin stays a shorthand `0`: at this width the box is already the
     full viewport, so the base rule's bleed margin-left resolves to 0
     anyway and nothing is lost by flattening it. */
  .weoffercontainer { margin: 0; padding-bottom: 70px; }

  h2.weofferheading, h3.weofferheading, h2.weofferheadingsmaller, h3.weofferheadingsmaller { font-size: 22px; padding: 15px 20px; line-height: 1.2em; }

  .weofferlogo {
    order: 3;
    flex: 1 1 100%;
    width: 100%;
    margin: 0 0 10px 0;
  }
  /* #contentWrapper prefix needed: the site-wide "#contentWrapper img { max-width:100% }"
     rule (section 1, an ID selector) otherwise beats this plain class selector on
     specificity regardless of source order, silently keeping width uncapped while
     max-height alone shrank the box — stretching/squishing the image.
     margin is !important because most of these <img> tags carry a raw inline
     margin (either "0px 0px 0px 0px" or a desktop/tablet bleed offset like
     "-10px 0px 0px -15px") straight from the original markup — only 5 pages
     use the logo-bleed-30/15 classes below, the other 900+ instances have no
     class to hook, so nothing short of !important can re-centre them on phones. */
  /* width:auto cancels the fluid `width: clamp(200px, 17vw, 310px)` set on
     the base .weofferlogo img rule, so max-width governs again on phones.
     Without it the clamp's 200px floor beat these caps — harmless for the
     130px case (max-width still won) but it silently shrank the 260px
     email-proof exception below to 200px. */
  #contentWrapper .weofferlogo img { width: auto; max-width: 130px; height: auto; margin: 0 auto !important; }

  /* "Free Photorealistic Email Proof" hand-holding-a-card icon (54 pages,
     filename "logo-realistic-email-proof-*") is a deliberate exception to
     the centring rule above: shown at double size and flush against the
     left edge of the viewport (not centred, not cropped off-screen),
     matching every page's shared markup regardless of whether that page
     uses the logo-bleed-30 class or a raw inline margin. */
  /* phones stack, so the icon has the full column width to itself and the
     wording sits below rather than beside it — no 310px slot to respect
     here, which is why this can go larger than the desktop figure. Raised
     from 260px to 300px alongside the desktop/tablet increase. */
  #contentWrapper .weofferlogo img[src*="logo-realistic-email-proof"] {
    width: auto;
    max-width: 300px !important;
    margin: -10px 0 0 0 !important;
  }

  /* bleed effect is desktop/tablet only — keep logos centred on phones
     (now redundant with the !important rule above, kept for clarity) */
  .weofferlogo img.logo-bleed-30,
  .weofferlogo img.logo-bleed-15 { margin: 0 auto; }

  .weofferwordingbase, div.weofferwording {
    order: 2;
    flex: 1 1 100%;
    margin: 15px 20px;
  }
  .weofferwording p { font-size: 16px; }
  .weofferwording a, .weofferwording span { font-size: 16px; line-height: 1.6em; }
  span.weofferwording32 { font-size: 20px; }
  span.weofferwording34 { font-size: 15px; }
  span.weofferwording54 { font-size: 15px; }

  .weofferfindoutmore { justify-content: center; padding: 0 20px 15px 20px; }

  .price-section { padding: 1rem 1rem 0 1rem; }

  .footercolumns { margin-top: 40px; gap: 10px; }
  .footercolumn { flex: 1 1 100%; text-align: center; }
  a.footerb:link, a.footerb:visited { line-height: 34px; font-size: 16px !important; }
  span.footerheading { font-size: 22px !important; line-height: 40px; }
  .mainfooter { padding: 0 20px; }

  /* Phone footer top row: the seals come OUT of the flex flow and sit in
     the top corners, which is exactly where the old background image
     painted them. Three flex columns don't work at this width — the seals
     take ~114px of the 335px available, leaving the middle column too
     narrow for the three social icons, which wrapped 2-then-1 (they sat
     on one row before this change, because the background artwork cost
     them no horizontal space).
     Absolute positioning gives that behaviour back: the icons and the
     payment-cards image get the full width again, and only the short
     "Email us" block near the top needs to stay clear of the seals —
     hence the max-width below, which is the same clear-band constraint
     the background version needed, for the same reason. */
  .footertop { position: relative; display: block; }
  .footerseal {
    position: absolute;
    top: 20px;
    width: 22%;
    max-width: 90px;
    margin-top: 0;
  }
  .footerseal-design { right: 0; }

  /* the clear band between the two corner seals, with a safety margin.
     The font-size clamps below shrink further so "sales@cpcards.co.uk"
     (unbreakable — no space to wrap on) still fits inside it. */
  .footeremailwrap {
    max-width: 52%;
    margin-left: auto;
    margin-right: auto;
  }
  a.footera:link, a.footera:visited { font-size: clamp(13px, calc(2.8125vw + 4px), 22px) !important; }
  span.footeremailnote { font-size: clamp(11px, calc(0.9375vw + 8px), 14px) !important; }
}

/* --------------------------------------------------------------------------
   10. Card-type picker page ("cards") — already responsive in source,
       ported as-is from style-cpcards-desktop.css
   -------------------------------------------------------------------------- */

:root {
  --ink: #14213D;
  --ink-soft: #3A4663;
  --paper: #F7F3EC;
  --paper-dark: #EFE8DA;
  --cyan: #0090C9;
  --cyan-dark: #006E9C;
  --grey: #5C5B57;
  --white: #FFFFFF;
  --radius: 30px;
}

.cardtypes-wrap { max-width: 1180px; margin: 0 auto; padding: 64px 24px 96px; }
.cardtypes-header { text-align: center; max-width: 700px; margin: 0 auto 56px; }
.cardtypes-eyebrow {
  display: inline-block;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cyan-dark);
  background: rgba(0,144,201,0.08);
  padding: 6px 14px;
  border-radius: 999px;
  margin-bottom: 18px;
}
.cardtypes-header h1 { font-family: 'DM Serif Display', serif; font-weight: 400; font-size: 42px; line-height: 1.15; margin: 0 0 16px; color: var(--ink); }
.cardtypes-header p { font-size: 17px; line-height: 1.6; color: var(--grey); margin: 0; }

.cardtypes-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }

@media (max-width: 900px) {
  .cardtypes-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .cardtypes-grid { grid-template-columns: 1fr; }
  .cardtypes-header h1 { font-size: 32px; }
}

.cardtype-tile {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-radius: var(--radius);
  padding: 32px 28px 28px 28px;
  text-decoration: none !important;
  color: inherit;
  position: relative;
  overflow: hidden;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.cardtype-tile:hover { transform: translateY(-4px); box-shadow: 0 16px 32px -16px rgba(20,33,61,0.25); }
.cardtype-tile:focus-visible { outline: 3px solid var(--cyan); outline-offset: 2px; }

.stack { position: relative; width: 100%; height: 88px; margin: 30px 0px 0px 30px; }
.stack .card { position: absolute; width: 96px; height: 60px; border-radius: 5px; top: 14px; left: 0; box-shadow: 0 6px 14px -6px rgba(20,33,61,0.35); transition: transform 0.3s ease; }
.stack .card.back { background: var(--paper-dark); transform: rotate(-9deg) translate(0px, 4px); border: 1px solid rgba(20,33,61,0.08); }
.stack .card.mid  { background: var(--white); transform: rotate(-3deg) translate(8px, 0px); border: 1px solid rgba(20,33,61,0.08); }
.stack .card.front{ background: var(--accent, var(--cyan)); transform: rotate(4deg) translate(20px, -4px); }

.cardtype-tile:hover .stack .card.back  { transform: rotate(-14deg) translate(-4px, 8px); }
.cardtype-tile:hover .stack .card.mid   { transform: rotate(-5deg) translate(10px, -2px); }
.cardtype-tile:hover .stack .card.front { transform: rotate(7deg) translate(26px, -8px) scale(150%); }

.cardtype-tile h2 { font-family: 'DM Serif Display', serif; font-weight: 400; font-size: 21px; margin: 0 0 8px; padding: 10px 30px 0px 30px; color: var(--ink); }
.cardtype-tile p { font-size: 14.5px; line-height: 1.55; color: var(--grey); margin: 0 0 20px; flex-grow: 1; padding: 0px 30px 10px 30px; }
.cardtype-tile .findmore { font-size: 13.5px; font-weight: 700; color: var(--cyan-dark); display: inline-flex; align-items: center; gap: 6px; padding: 0px 30px 10px 30px; }
.cardtype-tile .findmore svg { width: 14px; height: 14px; transition: transform 0.2s ease; }
.cardtype-tile:hover .findmore svg { transform: translateX(3px); }


/* --------------------------------------------------------------------------
   Wording boxes stop at the canvas edge above 1400px; headings still bleed
   --------------------------------------------------------------------------
   Split requested after a first pass capped everything: the HEADINGS should
   run off the page left and right again — the blue .headingfullwidth /
   .homeheadingfullwidth bars ("Membership Card Printing", "About our Plastic
   Membership Cards") and the dark-teal .weofferheading title bars ("Get your
   Quotation Now!") — while the BOXES OF WORDING under them (.weoffercontainer,
   the mint/sky-blue bodies) stop at the canvas.

   So only .weoffercontainer is capped here. The heading rules are deliberately
   absent from this block: they keep the width:var(--vw) + negative-margin
   bleed defined further up, which resolves against the viewport and is
   unaffected by the cap on the parent.

   Below 1401px nothing changes at all: there are no gutters, so the bleed and
   the cap resolve to the same box.

   Mechanics of the cap:
   - width becomes a flat 1400px. This must stay in step with
     #canvasWrapper's max-width, like the other hardcoded 1400px in this file.
   - margin-left calc(50% - 700px) is the same centring trick as the bleed
     rules, just against 1400px instead of the viewport: the 50% is of the
     containing block, so the box still breaks out of any inner padding, it
     just stops at the canvas rather than the window.
   - padding goes to 0. The base rule's
     calc((var(--vw) - min(var(--vw),1400px))/2) existed only to add the
     gutter width back so children stayed canvas-aligned; once the box is
     itself canvas-wide there is no gutter to compensate for, and the
     children still lay out across the same 1400px they always did.

   The flex-shrink override is the subtle part. .weofferheading is a flex
   item of .weoffercontainer with `flex: 1 1 var(--vw)`. Capping the parent
   at 1400px while the child asks for 1685px leaves negative free space, so
   flex-shrink:1 would pull the heading back to 1400px and silently undo its
   bleed — the heading would look capped even though nothing here targets it.
   flex-shrink:0 keeps it at its full viewport basis; it then overflows the
   parent, which is fine because .weoffercontainer is explicitly
   overflow:visible (see its rule).

   Rounded bottom corners on the boxes, also requested. Scoped to this
   media query on purpose: it is exactly where the box is inset from the
   viewport and reads as a card sitting under a full-width heading bar.
   Below 1401px the box runs edge to edge, and rounding the bottom corners
   against the screen edge there looks like a rendering fault, not a design.

   The header bands (#colheader, #colaboveheader, #colheadermenu,
   #colheaderspecialoffer) and .mainfooter are untouched and keep bleeding. */
@media (min-width: 1401px) {

  /* The .weoffercontainer cap that used to live here — width capped to the
     1400px canvas, side padding zeroed, and bottom corners rounded to 12px
     past the cap — has been REMOVED by later request: the wording boxes now
     bleed to the viewport edge at every width, square-cornered, exactly
     like the heading bars above them. Nothing replaces it; the box simply
     keeps the width:var(--vw) bleed from its base rule at all widths.
     Deliberately left as a note rather than deleted silently, because the
     block above still describes the capped behaviour at length and would
     otherwise read as describing live code.

     flex-shrink:0 below is NOT part of that cap and must stay. .weofferheading
     is a flex item with `flex: 1 1 var(--vw)`, while this container's content
     box is held at 1400px by its own gutter padding — so past the cap the
     heading's basis exceeds the space available and flex-shrink:1 would pull
     it back to 1400px, silently undoing the heading's own bleed. Keeping it
     at 0 lets the heading overflow the padding box, which is fine because
     .weoffercontainer is explicitly overflow:visible. */
  h2.weofferheading, h3.weofferheading,
  h2.weofferheadingsmaller, h3.weofferheadingsmaller {
    flex-shrink: 0;
  }
}


/* --------------------------------------------------------------------------
   Content images hover like the buttons
   --------------------------------------------------------------------------
   Requested: the card pictures on the example pages should use the same
   mouse-over animation as the quote/enquire buttons. Those images were on
   the old `.imagehover` treatment — a flat fade to `opacity: 0.8`, which
   dims the artwork rather than responding to the pointer.

   Values are copied verbatim from the button rules so the two stay in
   step: same 0.15s transform / 0.2s filter+shadow timing on the way in,
   same brightness(1.15) + translateY(-3px) scale(1.02) on hover, same
   brightness(0.95) + translateY(1px) + inner shadow on press.

   Scope is `main` — every page opens one (all 48 navigation/menu-*.php
   files do, verified), and it excludes exactly the two places this effect
   would look wrong: the header logo and the footer social icons. Those
   keep the original opacity fade from the rule near the top of this file.

   Two deliberate differences from the buttons:
   - The hover's `inset 0 1px 0 rgba(255,255,255,0.25)` highlight is
     dropped. Inset shadows paint below a replaced element's content, so
     an opaque photo covers it completely — carrying it over would be
     dead weight that only looks like a match in the source.
   - `opacity: 1 !important` is needed to cancel the existing
     `.imagehover:hover { opacity: 0.8 !important }`. The !important there
     means specificity alone would not win.

   Gated to >=1025px to match the existing .imagehover block: a lift-and-
   shadow state that sticks after a tap is worse than no effect at all on
   touch. */
@media screen and (min-width: 1025px) {

  /* Shadows here are `drop-shadow()` filters, NOT `box-shadow`. The card
     thumbnails are transparent .webp with rounded corners, and `box-shadow`
     paints the element's rectangular border box regardless of alpha — which
     boxed each card in a hard-edged panel on hover and hid the transparent
     corners. `drop-shadow()` follows the alpha channel, so the shadow traces
     the card's actual silhouette. The two shadow layers chain as two
     drop-shadow functions and keep the same offsets/blur as the buttons. */
  main .imagehover {
    transition: transform 0.15s ease,
                filter 0.2s ease,
                opacity 0.2s ease;
  }

  main .imagehover:hover {
    opacity: 1 !important;
    filter: brightness(1.15)
            drop-shadow(0 4px 8px rgba(0, 0, 0, 0.18))
            drop-shadow(0 10px 22px rgba(0, 0, 0, 0.22));
    transform: translateY(-3px) scale(1.02);
  }

  /* Pressed state: the old rule used an `inset` shadow, which has no
     drop-shadow equivalent and would have painted a dark rectangle across
     the transparent corners. A tighter shadow reads as "pressed" instead. */
  main a:active .imagehover {
    filter: brightness(0.95)
            drop-shadow(0 1px 1px rgba(0, 0, 0, 0.12));
    transform: translateY(1px);
    transition-duration: 0.05s;
  }
}
