/*==============================================================================
  Digi Kydo — mockup overrides

  Purpose: apply the SEO / accessibility fixes the migration spec requires
  WITHOUT changing a single rendered pixel versus the live site.

  Rule for anything in this file: if it moves a pixel, it does not belong here.
  Every rule below states which live-site measurement it is pinned to.
  Load last.
==============================================================================*/

/*------------------------------------------------------------------------------
  1. HEADING SEMANTICS
  The live site ships ZERO <h1> on all 111 pages and jumps straight to <h2>.
  The mockups introduce exactly one <h1> per page and a clean H1>H2>H3 chain.
  Because the theme sizes headings through .text-style-* / component classes
  rather than through the tag, most promotions render identically with no CSS.
  These three rules cover the cases where the tag itself carried style.
------------------------------------------------------------------------------*/

/* (a) The hero slide title becomes the homepage <h1>. The element it replaced
   was a <span>, which had no margin. theme.css gives every h1-h6 a
   margin-bottom of 1.5rem — cancel it so the hero grid is untouched.
   Measured target: .slide-title box 512x109 at (64,234) @1280px. */
.dynamic-slider h1.slide-title {
  margin: 0;
}

/* (b) The 21 decorative <h3>s inside the hero slide-info panel (7 service
   names repeated across 3 slides) are demoted to <span>. .slide-info-title
   already supplies font-size/weight/colour/margin; only `display` came from
   the h3 tag. Measured target: 16px / 400 / #fff, margin 0 0 0.5rem. */
.dynamic-slider span.slide-info-title {
  display: block;
  font-family: var(--secondary-font);
  line-height: 1.2;
}

/* (b2) The hamburger was a <div> on the live site — not focusable, not
   announced, not operable by keyboard. It is now a real <button>. These
   declarations only strip native button chrome; measured box stays 68x68. */
#menu-toggle.menu-toggle {
  padding: 0;
  border: 0;
  background: none;
  -webkit-appearance: none;
  appearance: none;
}
/* menu-grid.min.css positions .menu-toggle-icon absolutely with `top:auto`,
   which resolved to 0 when its parent was a <div> full of block children.
   Inside a <button> the static position picks up a line box, pushing it 22px
   down. Pinning top/left restores the measured 1181,25 68x68 box exactly. */
#menu-toggle .menu-toggle-icon {
  top: 0;
  left: 0;
}

/* (c) Long-form article headings. The live markup used h3/h5/h6 as visual
   sizes, so the tag cannot carry semantics any more. Sizes are now set by
   class and the tag is free to be correct.
   Measured on /service/seo/ inside .shock-section .content:
     h3 -> 28px / 700 / #1a1a20 / margin 32px 0 16px
     h5 -> 20px / 700 / #1a1a20 / margin 32px 0 16px
     h6 -> 16px / 700 / #1a1a20 / margin 32px 0 16px
   All at line-height 1.2 (theme.css). */
.shock-section .content h1,
.shock-section .content h2,
.shock-section .content h3,
.shock-section .content h4,
.shock-section .content h5,
.shock-section .content h6 {
  margin-top: 2rem;
  margin-bottom: 1rem;
  font-family: var(--secondary-font);
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-1);
}
/* Bootstrap 5 applies RFS to h1-h4 only. Live measurement: 28px at >=1200px,
   calc(1.3rem + 0.6vw) below that (23.05px at 375px). Reproduced exactly. */
.shock-section .content .hd-lg { font-size: calc(1.3rem + 0.6vw); } /* was h3 */
@media (min-width: 1200px) {
  .shock-section .content .hd-lg { font-size: 1.75rem; }
}
.shock-section .content .hd-md { font-size: 1.25rem; } /* was h5 */
.shock-section .content .hd-sm { font-size: 1rem; }    /* was h6 */

/* Sidebar block title: live markup used h5 (20px/700). Now h2 + .title. */
.shock-sidebar .sidebar-block .title {
  font-size: 1.25rem;
  line-height: 1.2;
}

/*------------------------------------------------------------------------------
  2. FOCUS VISIBILITY
  The live site renders no visible focus indicator anywhere. :focus-visible
  only fires for keyboard users, so pointer interaction is unchanged.
------------------------------------------------------------------------------*/
:focus-visible {
  outline: 2px solid var(--primary-color);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}
/* On the black footer and the black slide-info panel the primary blue is too
   low-contrast against #1a1a20 — use white there. */
.shock-footer :focus-visible,
.menu-grid :focus-visible,
.slide-info :focus-visible {
  outline-color: var(--white-color);
}

.skip-link {
  position: absolute;
  top: 0;
  left: -9999px;
  z-index: 9999;
  padding: 0.75rem 1.25rem;
  background-color: var(--black-color);
  color: var(--white-color);
  font-family: var(--secondary-font);
  font-size: 1rem;
  font-weight: 700;
}
.skip-link:focus {
  left: 0;
}

/* Visually hidden but crawlable / announced. Used only to complete the
   homepage <h1> string. Renders zero pixels. */
.sr-only:not(:focus):not(:active) {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*------------------------------------------------------------------------------
  3. IMAGES — prevent layout shift.
  Every <img> in the mockups carries intrinsic width/height. Bootstrap's
  `img { max-width: 100% }` is not applied globally by this theme, so pin
  aspect-ratio behaviour explicitly where the theme sizes images by CSS.
------------------------------------------------------------------------------*/
img {
  max-width: 100%;
}
/* The logos are sized by CSS height with an intrinsic 224x120 source, so the
   width attribute must not win. Measured live: 112x60 in header and footer. */
.shock-header .navbar .navbar-brand .logo,
.shock-footer .footer-content .footer-brand .logo {
  width: auto;
}

/*------------------------------------------------------------------------------
  4. FOOTER NAP BLOCK
  The only DELIBERATE visual addition in this set: a crawlable
  name-address-phone block. The live site hides the address inside a hamburger
  menu item as lowercase "17r dover Terrace Ballygunge" with no postcode.
  Styled to match the existing footer .nav-link.has-icon rows exactly
  (16px / Lato / #fff, 0.5rem vertical padding, 1rem icon, 10px gap), so it
  reads as one more line of the Contact Us list.
------------------------------------------------------------------------------*/
.shock-footer .footer-content address.nap {
  margin: 0;
  font-style: normal;
  font-size: 1rem;
  line-height: 1.5;
}
.shock-footer .footer-content .nav-link.has-icon .icon-svg {
  flex: 0 0 auto;
  margin-top: 2px;
  margin-right: 10px;
  width: auto;
  height: 1rem;
  fill: var(--gray-color);
  stroke: var(--gray-color);
  transition: all var(--duration-base) ease;
}
.shock-footer .footer-content .nav-link.has-icon:hover .icon-svg {
  fill: var(--color);
  stroke: var(--color);
}
.shock-footer .footer-content .nav-link.has-icon.is-address {
  align-items: flex-start;
}

/*------------------------------------------------------------------------------
  5. REDUCED MOTION
  The theme runs GSAP, AOS, lax parallax, anime.js and magnetic hover, none of
  which check the user's preference. (The SVG cursor follower that used to be
  in this list was deleted on 2026-07-29 — see main.css §1 Cursor.)
------------------------------------------------------------------------------*/
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  [data-aos] {
    opacity: 1 !important;
    transform: none !important;
  }
  .floating-item,
  .floating-item-smooth,
  .circular-text .emblem,
  .zzz {
    animation: none !important;
  }
  .zzz {
    width: 100% !important;
  }
}

/*------------------------------------------------------------------------------
  6. MOCKUP-ONLY SCAFFOLDING for the three pages that had to be AUTHORED
  (blog listing, blog post, 404) because the live theme's index.php,
  single.php and 404.php are literally empty files. Every declaration here
  re-uses a value already measured elsewhere on the live site — no new
  design language is introduced.
------------------------------------------------------------------------------*/

/* Blog card: same box treatment as a portfolio thumbnail
   (.rounded 6px + .small-shadow), same hover as .hover-zoom-rotate. */
.post-card {
  display: block;
  height: 100%;
}
.post-card .image-wrapper {
  overflow: hidden;
  margin-bottom: 1.5rem;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-small);
  aspect-ratio: 16 / 9;
}
.post-card .image-wrapper .image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: all var(--duration-slow) ease-out;
}
.post-card:hover .image-wrapper .image {
  transform: scale3d(1.1, 1.1, 1) rotate(2deg);
}
.post-card .post-meta {
  margin-bottom: 0.5rem;
  font-family: var(--secondary-font);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--gray-color);
}
.post-card .post-title {
  margin: 0 0 0.75rem;
  font-family: var(--secondary-font);
  font-size: var(--text-display-11); /* 19.2px / 700 — same as .text-style-11 */
  /* Reuses the .text-style-11 treatment, so it must reuse that step's tuned
     weight and tracking too — not a bare 700. With Hanken those tokens
     resolve to 700 / 0, i.e. exactly what this rule used to hard-code. */
  font-weight: var(--weight-display-11);
  letter-spacing: var(--tracking-display-11);
  line-height: 1.2;
  color: var(--black-color);
  transition: all var(--duration-base) ease;
}
.post-card:hover .post-title {
  color: var(--primary-color);
}
.post-card .post-excerpt {
  margin: 0;
  color: var(--gray-color);
}

/* Pagination: reuses the .badge outline chip, 29px tall, 4px radius. */
.pagination-list {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pagination-list li {
  margin: 0;
}
.pagination-list .page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 0.75rem;
  border: 1px solid var(--gray-50-color);
  border-radius: var(--radius-sm);
  font-family: var(--secondary-font);
  font-size: var(--text-small);
  color: var(--gray-color);
  transition: all var(--duration-base) ease;
}
.pagination-list .page-link:hover,
.pagination-list .page-link[aria-current="page"] {
  border-color: var(--secondary-color);
  background-color: var(--secondary-color);
  color: var(--white-color);
}

/* Article body on the blog post page = the exact .content treatment already
   measured on /service/seo/, so nothing new is invented. */
.post-article .post-meta {
  margin-bottom: 1.5rem;
  font-family: var(--secondary-font);
  font-size: var(--text-small);
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--gray-color);
}
.post-article .post-hero {
  width: 100%;
  height: auto;
  margin-bottom: 2rem;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-small);
}

/* 404 — reuses the page-title lockup (.text-style-2 outline + italic solid)
   and the standard .button.outline. No new components. */
.error-page {
  text-align: center;
}
.error-page .basic-intro .title {
  justify-content: center;
}
.error-page .helpful-links {
  margin: 2rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}
.error-page .helpful-links li {
  margin: 0;
}

/*------------------------------------------------------------------------------
  7. The modal close control was a bare <i> on the live site — not focusable and
  not announced. It is now a real <button> carrying the same .close-button
  class, so it keeps the measured position (absolute, top/right 1.75rem,
  1.5rem glyph). These three declarations only remove native button chrome.
------------------------------------------------------------------------------*/
.modal .modal-header button.close-button {
  border: 0;
  background: none;
  line-height: 1;
}

/* Blog card titles are headings, not body links — they must not pick up
   Bootstrap's default link blue. Colour + hover come from .post-card above,
   which reuses the measured .text-style-11 treatment (19.2px / 700 / #1a1a20,
   hover #509bbc). */
.post-card .post-title a {
  color: inherit;
}

/*------------------------------------------------------------------------------
  8. FONT SUBSTITUTION — Proxima Nova (Adobe Typekit) -> self-hosted OFL family
  Owner decision 2026-07-28: no webfont licence purchase, no Typekit.
  Owner decision 2026-07-28 (second): the display family is Montserrat.

  --secondary-font resolves to whichever VARIABLE family
  assets/fonts/display-font.css imports. Because neither substitute matches
  Proxima Nova's weight axis or its advance widths, each type-scale step gets a
  tuned wght and a tracking correction. Those values are NOT in tokens.css —
  they live with the family, in assets/fonts/montserrat.css (active) or
  assets/fonts/hanken-grotesk.css. Solved by measurement against the live
  Proxima Nova woff2 — see html/font-comparison.html.

  These rules DO move pixels, by design: they move them back toward the
  original Proxima Nova rendering. Without them the substitution is wrong.
  Layout is untouched: no width, no container, no grid changed here, and no
  font-size is changed either — apparent-size correction is done with
  size-adjust in the @font-face, so line boxes keep their authored geometry.
------------------------------------------------------------------------------*/
.text-style-1  { font-weight: var(--weight-display-1);  letter-spacing: var(--tracking-display-1); }
.text-style-2  { font-weight: var(--weight-display-2);  letter-spacing: var(--tracking-display-2); }
.text-style-3  { font-weight: var(--weight-display-3);  letter-spacing: var(--tracking-display-3); }
.text-style-4  { font-weight: var(--weight-display-4);  letter-spacing: var(--tracking-display-4); }
.text-style-5  { font-weight: var(--weight-display-5);  letter-spacing: var(--tracking-display-5); }
.text-style-6  { font-weight: var(--weight-display-6);  letter-spacing: var(--tracking-display-6); }
.text-style-7  { font-weight: var(--weight-display-7);  letter-spacing: var(--tracking-display-7); }
.text-style-8  { font-weight: var(--weight-display-8);  letter-spacing: var(--tracking-display-8); }
.text-style-9  { font-weight: var(--weight-display-9);  letter-spacing: var(--tracking-display-9); }
.text-style-10 { font-weight: var(--weight-display-10); letter-spacing: var(--tracking-display-10); }
.text-style-11 { font-weight: var(--weight-display-11); letter-spacing: var(--tracking-display-11); }
.text-style-12 { font-weight: var(--weight-display-12); letter-spacing: var(--tracking-display-12); }

/* The remaining --secondary-font type that is NOT a .text-style-* step. The
   Hanken build left these untuned because its error there was ~1%. Montserrat's
   is 13-20%, so they are tuned now; hanken-grotesk.css sets all five tracking
   values to 0 and the original Proxima weights, so swapping back reproduces
   the Hanken build exactly. */
.shock-section .content .hd-lg {
  font-weight: var(--weight-article-1);
  letter-spacing: var(--tracking-article-1);
}
.shock-section .content .hd-md {
  font-weight: var(--weight-article-2);
  letter-spacing: var(--tracking-article-2);
}
.shock-section .content .hd-sm {
  font-weight: var(--weight-article-3);
  letter-spacing: var(--tracking-article-3);
}
.shock-footer .footer-item .title {
  font-weight: var(--weight-footer-title);
  letter-spacing: var(--tracking-footer-title);
}
.menu-grid .menu-grid-minor-item,
.shock-footer .nav-list .nav-link {
  letter-spacing: var(--tracking-nav);
}

/* theme.css re-declares font-weight at <768px for .text-style-5 (800 -> 700)
   and .text-style-6 (inherits 100). The family file mirrors that shift on the
   variable axis (--weight-display-5/-6 inside its own max-width:767px block),
   so the mobile hero is not suddenly 100 units too heavy. Nothing to do here —
   the two rules above already read the responsive token. */

/*------------------------------------------------------------------------------
  9. 8px HORIZONTAL OVERFLOW AT 768px — FIXED
  Owner decision 2026-07-28: fix, do not preserve.

  Cause (measured, and it is NOT the rotated menu labels — the whole
  .menu-grid tree is clipped by overflow:hidden on .menu-grid,
  .menu-grid-item and .menu-grid-item-inner, so it contributes nothing to
  document.scrollWidth):

    core.css  @media (min-width:768px)  ->  .g-3 { --bs-gutter-x: 3rem }
              so Bootstrap's .row gets margin-left/right: -24px.
    main.css  @media (max-width:991px)  ->  .shock-footer .container
              { padding-left: 1rem; padding-right: 1rem }  i.e. 16px.

  In the 768-991px band the container is full-bleed (no auto margin slack),
  so 24px of negative row margin against 16px of container padding pushes the
  footer .row.g-3 to right: 776px on a 768px viewport. 776 - 768 = the 8px.
  Verified by hide-test: display:none on that one row drops scrollWidth
  768 <- 776 exactly.

  Fix: match the container padding to the half-gutter. Footer only — that is
  the only .row whose overflow is not already clipped by an ancestor, so it
  is the only one that reaches document.scrollWidth. Columns inside get 8px
  narrower each side in this band; nothing else moves, and the off-canvas
  menu is not touched at all.

  NOT used: overflow-x:hidden on html/body. That would mask the bug and
  would break the position:sticky sidebars driven by data-sticky /
  stickybits on the service and portfolio pages.
------------------------------------------------------------------------------*/
@media (min-width: 768px) and (max-width: 991px) {
  .shock-footer .container {
    padding-left: 1.5rem;  /* == .5 * --bs-gutter-x (3rem) for .g-3 */
    padding-right: 1.5rem;
  }
}

/*------------------------------------------------------------------------------
  10. SERVICE CARD ICONS — one stroke family.
  Pairs with resources/views/partials/service-icon.blade.php, which is the only
  place the eight glyphs are drawn. Read that file first.

  Before: eight <img> PNGs from the media library in three different
  treatments (teal line art, a solid black cart, a solid blue social glyph),
  six of the eight guessed from filenames. The owner: "the icons are not
  similar on all."

  Sizing is NOT redefined here on purpose. main.css `.card .card-image-icon`
  already sets height 50px / width auto / display block / margin-right auto,
  and editorial.css drops it to 38px inside `.ed-svc`. The SVG carries that
  same class, so the tiles' vertical rhythm is byte-identical to the PNGs it
  replaces. Only the PAINT changes below.

  main.css also sets `fill: var(--color); stroke: var(--color)` on that class,
  which would flood a stroke icon solid. Hence the .svc-icon compound
  selector (0,3,0 beats 0,2,0).

  COLOUR: --secondary-deep, #c4380f. Contrast measured, not eyeballed:
  5.36:1 on #fff and 4.83:1 on the #fdf1ea tint. WCAG 1.4.11 asks 3:1 of a
  meaningful graphic; these are decorative (aria-hidden) and still clear it
  with headroom. --secondary-color (#f95e1a) is 3.16:1 and is NOT used here —
  it is a surface tone, per the token contract in tokens.css.
------------------------------------------------------------------------------*/
.card .card-image-icon.svc-icon,
.card-image-icon.svc-icon {
  fill: none;
  stroke: currentColor;
  color: var(--secondary-deep);
  overflow: visible; /* the SEO magnifier's handle ends on the 21px gridline */
}

/* On an inverted / dark card the deep tone goes muddy; the surface tone is
   the correct one there and clears 3:1 against every dark scheme the theme
   ships. No card in the two templates this partial serves is dark today —
   this is here so that adding one cannot silently produce an invisible icon. */
.card.scheme-2 .card-image-icon.svc-icon,
.card.black .card-image-icon.svc-icon,
.bg-black .card .card-image-icon.svc-icon {
  color: var(--secondary-color);
}

/*------------------------------------------------------------------------------
  11. SCROLL-REVEAL FAIL-SAFE.
  aos.min.css sets `opacity: 0` on every [data-aos] element; something has to
  take it away, and if nothing does, the section is invisible. main.js §20
  watches for exactly that and sets this class on <html> when any [data-aos]
  element sits inside the viewport still computing opacity 0 for two seconds.

  `transition: none` is LOAD-BEARING, not tidiness. aos.min.css declares
  `transition-property: opacity, transform` on these elements, so without it
  flipping opacity here would START AN ANIMATION rather than apply a value —
  and an animation needs a rendering frame to progress. If the reason the
  section was stuck is that frames are not being produced, an animated fail-safe
  fails exactly as the thing it is rescuing. Killing the transition makes the
  reveal immediate and unconditional.

  A section that does not animate beats a section nobody can see.
------------------------------------------------------------------------------*/
html.aos-unavailable [data-aos] {
  transition: none !important;
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}
