/* ==========================================================================
   Redvent — design system layer
   --------------------------------------------------------------------------
   The pages are prototypes written with inline styles. This sheet is the one
   place where the system lives: colour roles, elevation, focus, motion,
   interaction and responsive density. It is loaded last in every page head
   and uses `!important` only where it must beat an inline declaration.

   Brand is fixed. #ED1F24 stays the identity red. Because white-on-#ED1F24
   measures 4.35:1 (below the 4.5:1 AA threshold for the 14-16px labels the
   site uses), the red is expressed as three roles of the same hue:

     --red        #ED1F24  identity, logo, decoration, icons, red-on-black
     --red-fill   #DC1217  solid fills that carry a white label   (5.07:1)
     --red-ink    #C8161B  red set as text on a light ground      (5.02:1+)

   Nothing else about the brand changes.
   ========================================================================== */

:root{
  /* ---- colour roles ---- */
  --red:        #ED1F24;
  --red-fill:   #DC1217;
  --red-hover:  #C8161B;
  --red-press:  #A80E12;
  --red-ink:    #C8161B;
  --red-wash:   #FDECEC;

  --green:      #69BD45;   /* identity / dark grounds only */
  --green-icon: #3E8F25;   /* graphics on light (4.07:1) */
  --green-ink:  #33771C;   /* green set as text on light (4.95:1+) */
  --green-wash: #EAF6E4;
  --green-lift: #8FD86E;   /* on near-black */

  --ink:        #0B0B0C;
  --ink-2:      #1A1A1C;
  --body:       #5C5C58;
  --muted:      #6A6762;   /* was #8A8783 — 3.37:1, failed AA */
  --muted-dark: #B4B4AF;   /* muted on near-black */

  --paper:      #FFFFFF;
  --warm:       #FAF8F6;
  --warm-2:     #F5F3F0;
  --line:       #F0EDEA;
  --line-2:     #E6E2DE;

  /* ---- elevation ramp (5 steps, replaces 46 ad-hoc shadows) ---- */
  --e1: 0 1px 2px rgba(11,11,12,.05);
  --e2: 0 1px 2px rgba(11,11,12,.05), 0 6px 16px rgba(11,11,12,.06);
  --e3: 0 2px 4px rgba(11,11,12,.04), 0 14px 32px rgba(11,11,12,.09);
  --e4: 0 4px 10px rgba(11,11,12,.05), 0 28px 60px rgba(11,11,12,.13);
  --e-red: 0 1px 2px rgba(11,11,12,.10), 0 8px 20px rgba(200,22,27,.22);

  /* ---- motion ---- */
  --ease: cubic-bezier(.2,.7,.3,1);
  --fast: .16s;
  --mid:  .26s;
}

/* ==========================================================================
   1. Typography
   ========================================================================== */

/* Balanced headline rag. The inline styles never set text-wrap, so this wins
   without !important and fixes single-word orphans at every breakpoint. */
h1,h2,h3{ text-wrap: balance; }
h4,h5,h6,p,li,dd,figcaption{ text-wrap: pretty; }

/* Prices and figures line up in columns. */
[data-rv="price"], [data-rv="price"] *{ font-variant-numeric: tabular-nums; }

/* Comfortable measure for the centred section leads, which otherwise run to
   the full 720px container and lose the reader between lines. */
[data-rv="lead"]{ max-width: 58ch; margin-left:auto; margin-right:auto; }

/* Superscript-free currency: keep the R tight to the number. */
[data-rv="price"]{ letter-spacing: -.015em; }

/* ==========================================================================
   2. Colour roles applied over the inline styles
   --------------------------------------------------------------------------
   The prototype runtime renders through React, so inline styles reach the DOM
   serialised as `color: rgb(r, g, b)`. Selectors below match that form.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Ground-aware roles.

   #8A8783 measures 3.37:1 on the warm ground and 5.50:1 on near-black; the
   accessible replacement for one is the wrong colour for the other. Ancestor
   selectors cannot resolve this because white cards sit inside black bands on
   six pages. Custom properties can: they inherit, so the nearest painted
   surface always wins. redvent.js stamps data-rv-ground on every element that
   paints an opaque background, including the ones bound to component state.
   -------------------------------------------------------------------------- */

:root,
[data-rv="on-paper"],
[data-rv-ground="paper"]{
  --role-muted:      #6A6762;   /* 4.70:1 – 5.63:1 on the light grounds */
  --role-red:        #C8161B;   /* 5.02:1 – 5.86:1 */
  --role-green:      #33771C;   /* 4.95:1 on the mint pill, 5.53:1 on white */
  --role-green-icon: #3E8F25;   /* 4.07:1 — graphics need 3:1 */
}
[data-rv="on-ink"],
[data-rv-ground="ink"]{
  --role-muted:      #A8A8A3;   /* 8.24:1 on #0B0B0C */
  --role-red:        #ED1F24;   /* 4.52:1 — the identity red is correct here */
  --role-green:      #8FD86E;   /* 11.44:1 */
  --role-green-icon: #69BD45;
}

[style*="color: rgb(138, 135, 131)"],   /* #8A8783 */
[style*="color: rgb(155, 150, 142)"],   /* #9B968E */
[style*="color: rgb(122, 122, 117)"]{   /* #7A7A75 */
  color: var(--role-muted) !important;
}
[stroke="#8A8783"]{ stroke: var(--role-muted); }

[style*="color: rgb(237, 31, 36)"]{ color: var(--role-red) !important; }

[style*="color: rgb(62, 143, 37)"],     /* #3E8F25 */
[style*="color: rgb(105, 189, 69)"]{    /* #69BD45 as text */
  color: var(--role-green) !important;
}
[stroke="#69BD45"]{ stroke: var(--role-green-icon); }

/* --- red as a solid fill behind a white label (4.35:1 -> 5.07:1) --- */
[style*="background: rgb(237, 31, 36)"]{ background-color: var(--red-fill) !important; }
/* decorative dots and rules keep the identity red */
[data-rv="brand-dot"]{ background-color: var(--red) !important; }

/* A red-tinted shadow under a red button reads as depth on a light ground and
   as an AI glow on a dark one, where a shadow does nothing anyway. */
[data-rv-ground="ink"] [style*="rgba(237, 31, 36, 0.32)"],
[data-rv-ground="ink"] [style*="rgba(237, 31, 36, 0.3)"],
[data-rv-ground="ink"] [style*="rgba(237, 31, 36, 0.35)"],
[data-rv-ground="ink"] [style*="rgba(237, 31, 36, 0.28)"],
[data-rv-ground="ink"] [style*="rgba(237, 31, 36, 0.25)"]{ box-shadow: none !important; }

/* ==========================================================================
   3. Elevation — collapse 46 ad-hoc shadows onto a five-step ramp
   ========================================================================== */

/* The heavy red glow under every primary button was the loudest thing on the
   page and read as decoration rather than as an affordance. Tighten it. */
[style*="rgba(237, 31, 36, 0.32)"],
[style*="rgba(237, 31, 36, 0.3)"],
[style*="rgba(237, 31, 36, 0.35)"],
[style*="rgba(237, 31, 36, 0.28)"],
[style*="rgba(237, 31, 36, 0.25)"]{ box-shadow: var(--e-red) !important; }

[style*="0px 30px 70px"],
[style*="0px 24px 56px"]{ box-shadow: var(--e4) !important; }

[style*="0px 18px 44px"],
[style*="0px 18px 40px"],
[style*="0px 20px 50px"],
[style*="0px 14px 34px"],
[style*="0px 16px 36px"]{ box-shadow: var(--e3) !important; }

[style*="0px 1px 12px"]{ box-shadow: var(--e1) !important; }

/* The same for hover elevations the runtime compiles into generated classes. */
a:hover[style*="0px 16px 36px"]{ box-shadow: var(--e4) !important; }

/* ==========================================================================
   4. Focus — one visible ring everywhere, on light and on dark
   ========================================================================== */

a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible{
  outline: 2px solid var(--red-fill) !important;
  outline-offset: 3px !important;
  border-radius: 6px;
}
input:focus-visible, select:focus-visible, textarea:focus-visible{
  outline: 2px solid var(--red-fill) !important;
  outline-offset: 2px !important;
}
/* On near-black panels a red ring on black is hard to see; add a light halo. */
[data-rv-ground="ink"] a:focus-visible,
[data-rv-ground="ink"] button:focus-visible,
[data-rv="on-ink"] a:focus-visible,
[data-rv="on-ink"] button:focus-visible{
  outline-color: #FFFFFF !important;
  box-shadow: 0 0 0 5px rgba(237,31,36,.55) !important;
}
/* ...and back to the brand ring on a light card inside a dark band. */
[data-rv-ground="paper"] a:focus-visible,
[data-rv-ground="paper"] button:focus-visible{
  outline-color: var(--red-fill) !important;
  box-shadow: none !important;
}

/* Skip link. Moved on transform rather than `top` so it never triggers layout. */
[data-rv="skip"]{
  position:absolute; left:12px; top:12px; z-index:200;
  background:var(--ink); color:#fff; padding:12px 18px; border-radius:10px;
  font-size:14px; font-weight:600;
  transform:translateY(-140%); transition:transform var(--fast) var(--ease);
}
[data-rv="skip"]:focus{ transform:translateY(0); }

::selection{ background: rgba(237,31,36,.16); color: var(--ink); }

/* The off-canvas drawer is fixed and translated 100% to the right, but Chrome
   still counts its contents in the document's scrollable width, so every page
   could be swiped ~200px sideways on a phone. `clip` removes the sideways
   scroll without creating a scroll container, so position:sticky still works.
   The QA sweep checks element right-edges rather than scrollWidth, so this
   does not hide a genuine overflow that appears later. */
html{ overflow-x: clip; }

/* Grid and flex children default to min-width:auto, so a track sized 1fr can
   still resolve wider than its container when the content has a large
   min-content width. On a 375px phone that pushed the Home enquiry block to
   556px and the Email Hosting hero to 390px. Every responsive grid on the site
   carries a data-r hook, so one rule closes the whole class. */
[data-r] > *{ min-width: 0; }

/* Email addresses, domains and long product codes are unbreakable strings and
   pushed their column past the viewport on a phone. */
@media (max-width: 760px){
  body{ overflow-wrap: anywhere; }
  h1,h2,h3{ overflow-wrap: break-word; }
}

/* ==========================================================================
   5. Interaction — press states and card behaviour
   ========================================================================== */

/* Every card in the site lifts on hover but had no press state, so a tap on a
   phone gave no feedback at all. */
a[style*="border-radius: 18px"],
a[style*="border-radius: 16px"],
a[style*="border-radius: 14px"],
a[style*="border-radius: 20px"]{
  transition: transform var(--mid) var(--ease), box-shadow var(--mid) var(--ease), border-color var(--fast) var(--ease) !important;
}
a:active, button:active{ transform: translateY(1px) scale(.995); }
button:disabled:active{ transform: none; }

/* A disabled control must not brighten to the primary colour on hover; the
   generated hover classes did exactly that on sold-out Add to Cart buttons. */
button[disabled], button[disabled]:hover{
  background: #6A6762 !important;
  cursor: not-allowed;
  box-shadow: none !important;
}

/* Photographs inside cards gain a slow, quiet scale so hover reads as depth
   rather than as the whole card jumping. */
[data-rv="card"] image-slot{ transition: transform .5s var(--ease); }
[data-rv="card"]:hover image-slot{ transform: scale(1.035); }

/* Arrow affordances actually move. */
[data-rv="go"] svg{ transition: transform var(--mid) var(--ease); }
[data-rv="go"]:hover svg, a:hover [data-rv="go"] svg{ transform: translateX(4px); }

/* Inline text links in prose get an underline instead of relying on red alone
   to signal that they are links. */
p a[href]:not([style*="border-radius"]):not([style*="padding"]),
li a[href]:not([style*="border-radius"]):not([style*="padding"]){
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(200,22,27,.4);
}
p a[href]:hover, li a[href]:hover{ text-decoration-color: currentColor; }

/* ==========================================================================
   6. Motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce){
  /* The doubled attribute selectors raise specificity above the interaction
     rules in section 5, which carry !important to beat the inline styles. A
     plain universal selector lost that fight and left 18 live transitions. */
  *, *::before, *::after,
  [style][style], [class][class], [data-rv][data-rv], [data-r][data-r]{
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  html, html body{ scroll-behavior: auto !important; }
}

/* ==========================================================================
   7. Responsive density
   --------------------------------------------------------------------------
   At 375px the homepage measured 21,967px tall — roughly 27 screens — because
   every one of ~35 stacked cards carried a full-width photograph. Secondary
   card strips become snap rails on small screens: the imagery survives, the
   scroll does not.
   ========================================================================== */

@media (max-width: 760px){
  [data-rv="rail"]{
    display: flex !important;
    grid-template-columns: none !important;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 -22px !important;
    padding: 2px 22px 14px !important;
  }
  [data-rv="rail"]::-webkit-scrollbar{ display:none; }
  [data-rv="rail"] > *{
    flex: 0 0 76%;
    scroll-snap-align: start;
  }
  [data-rv="rail"][data-rv-w="narrow"] > *{ flex: 0 0 58%; }

  /* Hero call-to-actions fill the column and stack tight. */
  [data-rv="hero-cta"]{
    display: grid !important;
    grid-template-columns: 1fr !important;
    gap: 10px !important;
  }
  [data-rv="hero-cta"] > a{
    width: 100%;
    justify-content: center !important;
  }

  /* Trim the uniform 60px band padding on phones. */
  [data-rv="band"]{ padding-top: 44px !important; padding-bottom: 44px !important; }

  /* Three facts across one row needs the full container; on a phone it is a
     two-up, then a single column when the strings no longer fit. */
  [data-rv="specbar"]{ grid-template-columns: 1fr 1fr !important; gap: 12px 18px !important; }


  /* Split panels stop being 50/50 heroes and become a readable stack. */
  [data-rv="stack-tight"]{ gap: 22px !important; }
}

/* A decorative 0/16/32px stagger is a desktop flourish. Once the split
   collapses at 980px the cards are full-width and the third one simply hung
   32px off the right edge. */
@media (max-width: 980px){
  [data-rv="stagger"]{ transform: none !important; }
}

@media (max-width: 640px){
  /* Photo-led navigation cards become rows. Six stacked cards with 16:10
     photographs ran 2,600px on a phone; as rows they run about 800px and the
     photograph still does its job at thumbnail size. */
  [data-rv="card"]{
    flex-direction: row !important;
    align-items: stretch !important;
  }
  [data-rv="card"] > div:first-child{
    width: 116px !important;
    flex: none !important;
    aspect-ratio: auto !important;
    align-self: stretch;
  }
  [data-rv="card"] > div:last-child{
    padding: 15px 16px 15px 17px !important;
    justify-content: center;
  }
  /* The icon badge overhangs the photograph on desktop; in a row it would
     collide with the heading, and the photograph already identifies the card. */
  [data-rv="card"] > div:last-child > span:first-child{ display: none !important; }
  [data-rv="card"] h3{ font-size: 16px !important; margin-bottom: 5px !important; }
  [data-rv="card"] p{ margin-bottom: 9px !important; font-size: 14px !important; }
}

@media (max-width: 520px){
  [data-rv="rail"] > *{ flex: 0 0 82%; }
  [data-rv="rail"][data-rv-w="narrow"] > *{ flex: 0 0 66%; }
  [data-rv="specbar"]{ grid-template-columns: 1fr !important; }
}

/* ==========================================================================
   8. Header and footer corrections
   ========================================================================== */

/* Utility bar links were 15px tall — well under the 24px minimum target. */
[data-rh="topbar"] a{
  min-height: 30px;
  display: inline-flex;
  align-items: center;
}

/* Footer navigation is a stacked list, not prose, so the WCAG inline-link
   exception does not apply to it. */
footer nav a, footer li a{
  display: inline-flex;
  align-items: center;
  min-height: 26px;
}

/* Breadcrumbs are navigation, not a sentence: they measured 16px tall, and on
   a 375px phone the trail was 390px wide and pushed the page sideways. */
nav[aria-label="Breadcrumb"]{ flex-wrap: wrap; row-gap: 2px; }
nav[aria-label="Breadcrumb"] a{
  display: inline-flex;
  align-items: center;
  min-height: 26px;
}

/* A link that is a direct child of a flex or grid container is a standalone
   action ("Book this →", a product title), not a link inside a sentence, so
   the WCAG inline exception does not cover it. Anything with its own padding
   is already a button and is left alone. */
[style*="display: flex"] > a:not([style*="padding"]):not([style*="width:"]),
[style*="display: grid"] > a:not([style*="padding"]):not([style*="width:"]){
  min-height: 26px;
  display: inline-flex;
  align-items: center;
}

/* The "Request this →" style card calls are real actions with no padding of
   their own, so they came in at 16px. The inline-link exception covers links
   inside running prose, which these are not. */
a[style*="display: inline-flex"]:not([style*="padding"]):not([style*="width:"]),
a[style*="align-self: flex-start"],
a[href^="tel:"],
a[style*="text-decoration: underline"]:not(p a):not(li a){
  display: inline-flex;
  align-items: center;
  min-height: 26px;
}
/* ...except a phone number written inside a sentence or a postal address. */
p a[href^="tel:"], address a[href^="tel:"], li a[href^="tel:"]{
  display: inline;
  min-height: 0;
}

/* Checkboxes were 18px. Blowing the box itself up to 24px turned every shop
   filter into a heavy red square, so the box grows only a little and the label
   around it carries the target area — which is what a visitor actually taps. */
input[type="checkbox"][style], input[type="radio"][style]{
  width: 20px !important;
  height: 20px !important;
  min-width: 20px;
  padding: 0;
  outline-offset: 3px;
}
/* Range sliders came in at 16px tall. */
input[type="range"]{ height: 26px; }
label:has(input[type="checkbox"]), label:has(input[type="radio"]){
  min-height: 32px;
  align-items: center;
  cursor: pointer;
}

/* ==========================================================================
   9. Forms
   ========================================================================== */

input[style], select[style], textarea[style]{
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
input[style]:hover, select[style]:hover, textarea[style]:hover{ border-color: #C9C4BE !important; }
input[style]:focus, select[style]:focus, textarea[style]:focus{
  border-color: var(--red-fill) !important;
}
::placeholder{ color: var(--muted); opacity: 1; }

/* Native select arrow, so the control does not read as a dead text field. */
select[style]{
  appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236A6762' stroke-width='2.2' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>") !important;
  background-repeat: no-repeat !important;
  background-position: right 14px center !important;
  padding-right: 40px !important;
}

/* ==========================================================================
   10. Image slots
   ========================================================================== */

/* Slots with no photograph behind them must not read as a broken image; give
   them a quiet, deliberate surface instead of a dashed frame. */
image-slot{ background: transparent; }

/* ==========================================================================
   11. Print
   ========================================================================== */

@media print{
  header, footer, [data-rv="skip"], aside{ display:none !important; }
  *{ box-shadow:none !important; }
  a[href^="http"]::after{ content:" (" attr(href) ")"; font-size:11px; color:#666; }
}

/* ---- Redvent optional-services price list (2026-09-04) ----------------
   The two-column price grid was built desktop-first with no narrow-width
   rule, so at 390px the fixed 1fr/1fr columns squeezed each row down to
   ~150px and long service names ("Website maintenance") wrapped one
   letter per line. Below 640px it collapses to a single column and each
   row stacks name-over-value instead of side-by-side. */
@media (max-width: 640px){
  [data-rf="pricelist"]{ grid-template-columns: 1fr !important; }
  [data-rf="pricelist"] > div{ border-right: 0 !important; }
  [data-rf="pricelist"] > div > div{
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 3px !important;
  }
  [data-rf="pricelist"] > div > div > span:last-child{
    text-align: left !important;
  }
}
