/* canbell.com — shared styles
   Tokens and rules follow docs/DESIGN-SPEC.md. No magic numbers: every colour,
   space and type step below resolves to a custom property. */

:root {
  /* Colour — sampled from the logo. See DESIGN-SPEC.md §2. */
  --paper: #ffffff;
  --ink: #12131a;
  --muted: #6b6f80;
  --brand: #0040f0;
  --brand-deep: #002068;
  --mist: #eef1fb;
  --hairline: #e3e5f0;

  /* Type */
  --font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;

  --size-display: clamp(2.4rem, 6vw, 4rem);
  --size-h2: clamp(1.75rem, 3.5vw, 2.5rem);
  --size-h3: clamp(1.05rem, 1.5vw, 1.25rem);
  --size-body: 1.0625rem;
  --size-small: 0.95rem;
  --size-mono: 0.8rem;

  /* Space — 8px base */
  --s-1: 0.5rem;
  --s-2: 1rem;
  --s-3: 1.5rem;
  --s-4: 2rem;
  --s-5: 3rem;
  --s-6: 4rem;
  --section-y: clamp(4rem, 9vw, 7rem);
  --gutter: clamp(1.25rem, 4vw, 3rem);

  --measure: 68ch;
  --container: 1200px;
  --radius: 8px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html {
  color-scheme: light;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--size-body);
  line-height: 1.6;
  overflow-x: hidden;
}

img, svg { max-width: 100%; }
img { height: auto; }

a { color: var(--brand); }

/* Focus is never removed, only replaced. DESIGN-SPEC.md §8. */
:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: var(--s-2);
  top: var(--s-2);
  z-index: 100;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--s-1) var(--s-2);
  background: var(--brand);
  color: #fff;
  font-weight: 500;
  border-radius: var(--radius);
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---------- Type ---------- */

.display {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--size-display);
  line-height: 1.05;
  letter-spacing: -0.01em;
}

h2 {
  font-size: var(--size-h2);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
}

h3 {
  font-size: var(--size-h3);
  font-weight: 600;
  line-height: 1.3;
}

.lede, p { max-width: var(--measure); }
.lede {
  font-size: clamp(1.05rem, 1.8vw, 1.25rem);
  color: var(--muted);
}

/* The mono layer. Texture only — never body copy. DESIGN-SPEC.md §3. */
.mono {
  font-family: var(--font-mono);
  font-size: var(--size-mono);
  letter-spacing: 0.06em;
  color: var(--muted);
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--size-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand);
  display: block;
  margin-bottom: var(--s-2);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  min-height: 44px;
  padding: 0.7rem 1.4rem;
  border-radius: var(--radius);
  font-size: var(--size-small);
  font-weight: 500;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-deep); }
.btn-secondary {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--hairline);
}
.btn-secondary:hover { background: var(--mist); border-color: var(--brand); }

/* ---------- Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hairline);
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  min-height: 68px;
}
.brand-link { display: flex; align-items: center; min-height: 44px; flex-shrink: 0; }
.brand-link img { width: clamp(124px, 22vw, 168px); height: auto; display: block; }

.site-nav { display: flex; align-items: center; gap: clamp(0.5rem, 2vw, 1.5rem); }
.site-nav a {
  color: var(--ink);
  text-decoration: none;
  font-size: var(--size-small);
  font-weight: 500;
  padding: 0.6rem 0.4rem;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--brand); }
.site-nav a[aria-current='page'] { border-bottom-color: var(--brand); }

/* The phone number never collapses into a menu. DESIGN-SPEC.md §5. */
.header-phone {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  font-weight: 600;
  font-size: var(--size-small);
  color: var(--brand);
  text-decoration: none;
  white-space: nowrap;
}
.header-phone:hover { background: var(--mist); border-color: var(--brand); }

/* Below ~560px the logo, two nav links and the phone number cannot share one
   row. The nav drops to a second row rather than the phone being clipped or
   hidden. The header also stops sticking here — two rows of sticky chrome eats
   an eighth of a phone screen, and the hero's Call button is immediately below
   anyway. */
@media (max-width: 559px) {
  .site-header { position: static; }
  .site-header .container {
    flex-wrap: wrap;
    justify-content: center;
    padding-block: var(--s-1);
    min-height: 0;
  }
  .site-nav {
    width: 100%;
    justify-content: space-between;
    gap: var(--s-1);
  }
}

/* ---------- Sections ---------- */

section { padding-block: var(--section-y); }
.section-tint { background: var(--mist); }
.section-head { margin-bottom: var(--s-5); }
.section-head p { margin-top: var(--s-2); color: var(--muted); }

/* ---------- Hero ---------- */

.hero { padding-block: clamp(3rem, 7vw, 5.5rem) var(--section-y); }
.hero-grid {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
@media (min-width: 1024px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}
.hero h1 { margin-bottom: var(--s-3); }
.hero .lede { margin-bottom: var(--s-4); }
.hero-address { font-style: normal; display: block; margin-bottom: var(--s-3); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-4); }
.hero-range {
  display: inline-block;
  padding-top: var(--s-3);
  border-top: 1px solid var(--hairline);
  width: 100%;
}

/* ---------- Hero photo (Canbell's own build) ---------- */

.rig {
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: clamp(1rem, 3vw, 1.75rem);
  background: var(--paper);
}

.rig-stage {
  position: relative;
  /* Matches the shared 960x958 photo frame — see assets/hero/. */
  aspect-ratio: 960 / 958;
}
.rig-photo { display: block; width: 100%; height: 100%; object-fit: contain; }

/* Each part sits at its true position in the base photo — computed once from
   the source images' alpha bounds, not eyeballed. --ox/--oy (set inline per
   button) is the "pulled out of the case" offset, as a % of the part's own
   box so it scales with the card at any width. */
.rig-hotspot {
  position: absolute;
  left: var(--l);
  top: var(--t);
  width: var(--w);
  height: var(--h);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transform: translate(0, 0) scale(1);
  transition: transform 0.55s cubic-bezier(0.34, 1.35, 0.4, 1);
  -webkit-tap-highlight-color: transparent;
}
.rig-hotspot img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* The reference photo shows the CPU fully seated under the cooler — no sliver
   of it is meant to be visible at rest. z-index cannot fix this: the cooler's
   own silhouette has open gaps (the fan blades) that the CPU's photo shows
   through no matter which layer paints on top. So the CPU hotspot stays
   invisible at rest and only fades in on hover/focus/extraction, as though
   pulling the cooler aside is what reveals it — which is also true of a real
   machine. */
.rig-hotspot[data-target='cpu'] img { opacity: 0; transition: opacity 0.3s ease; }
.rig-hotspot[data-target='cpu']:hover img,
.rig-hotspot[data-target='cpu']:focus-visible img,
.rig-hotspot[data-target='cpu'].is-out img { opacity: 1; }

.rig-hotspot:hover,
.rig-hotspot:focus-visible { transform: translate(0, 0) scale(1.06); z-index: 6; }
.rig-hotspot.is-out {
  transform: translate(var(--ox), var(--oy)) scale(1.08);
  z-index: 9;
  filter: drop-shadow(0 14px 22px rgba(18, 19, 26, 0.28));
}

/* A soft halo behind each hotspot — a static ring for hover, focus and the
   extracted state. */
.rig-hotspot::before {
  content: '';
  position: absolute;
  inset: -10%;
  border-radius: 999px;
  background: radial-gradient(circle, rgba(0, 64, 240, 0.22), rgba(0, 64, 240, 0) 70%);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.rig-hotspot:hover::before,
.rig-hotspot:focus-visible::before,
.rig-hotspot.is-out::before { opacity: 1; }

/* Before the visitor has touched the hero at all, each part's own silhouette
   glows in turn — edge light, not a background blob, so it reads as the part
   itself lighting up. One glows for ~1s, a half-second gap, then the next;
   six parts make one loop 9s long. Most parts sit inside the dark case
   cavity, so the glow has to be strong enough to read against near-black
   pixels, not just against the white page — a light scale pulse rides along
   so the eye catches motion even where the colour contrast is weak. */
@media (prefers-reduced-motion: no-preference) {
  .rig:not(.is-engaged) .rig-hotspot:not([data-target='cpu']) img {
    animation: rig-invite 9s ease-in-out infinite;
    animation-delay: calc(var(--i) * 1.5s);
    transform-origin: center;
  }
  .rig:not(.is-engaged) .rig-hotspot[data-target='cpu'] img {
    animation: rig-invite-reveal 9s ease-in-out infinite;
    animation-delay: calc(var(--i) * 1.5s);
    transform-origin: center;
  }
}
@keyframes rig-invite {
  0%, 11.2%, 100% {
    filter: drop-shadow(0 0 0 rgba(0, 64, 240, 0));
    transform: scale(1);
  }
  5.5% {
    filter: drop-shadow(0 0 4px rgba(130, 170, 255, 1))
            drop-shadow(0 0 16px rgba(0, 64, 240, 0.95))
            drop-shadow(0 0 34px rgba(0, 64, 240, 0.75))
            drop-shadow(0 0 56px rgba(0, 64, 240, 0.4));
    transform: scale(1.045);
  }
}
/* Same glow, but for the CPU it also has to bring the part into view — it is
   opacity: 0 at rest (see above), so its one moment in the loop is also the
   only moment it is visible at all. */
@keyframes rig-invite-reveal {
  0%, 11.2%, 100% {
    filter: drop-shadow(0 0 0 rgba(0, 64, 240, 0));
    transform: scale(1);
    opacity: 0;
  }
  5.5% {
    filter: drop-shadow(0 0 4px rgba(130, 170, 255, 1))
            drop-shadow(0 0 16px rgba(0, 64, 240, 0.95))
            drop-shadow(0 0 34px rgba(0, 64, 240, 0.75))
            drop-shadow(0 0 56px rgba(0, 64, 240, 0.4));
    transform: scale(1.045);
    opacity: 1;
  }
}

.rig-readout {
  margin-top: var(--s-2);
  min-height: 3.2em;
  font-size: var(--size-small);
  color: var(--muted);
}
.rig-readout strong { color: var(--ink); font-weight: 600; }

/* ---------- Cards ---------- */

.grid {
  display: grid;
  gap: var(--s-2);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: var(--s-3);
  transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
}
.card:hover {
  border-color: var(--brand);
  box-shadow: 0 16px 34px -18px rgba(0, 64, 240, 0.5), 0 0 0 1px rgba(0, 64, 240, 0.35);
}
@media (prefers-reduced-motion: no-preference) {
  .card:hover {
    transform: perspective(700px) rotateX(2deg) rotateY(-2deg) translateY(-3px);
  }
}
.card .mono { display: block; margin-bottom: var(--s-2); }
.card h3 { margin-bottom: var(--s-1); }
.card p { color: var(--muted); font-size: var(--size-small); }

.more-categories { border: 0; margin-top: var(--s-2); }
.more-categories summary { justify-content: flex-start; }

/* ---------- Trust strip ---------- */

.strip {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
}
@media (min-width: 768px) { .strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.strip .num {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(2rem, 4vw, 2.75rem);
  line-height: 1;
  color: var(--brand-deep);
  display: block;
  margin-bottom: var(--s-1);
}
.strip p { color: var(--muted); font-size: var(--size-small); }

/* ---------- Contact ---------- */

.contact-grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: start;
}
@media (min-width: 900px) { .contact-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); } }

address { font-style: normal; }
.detail-list { list-style: none; display: grid; gap: var(--s-3); }
.detail-list .mono { display: block; margin-bottom: 0.15rem; }
.detail-list a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--hairline); }
.detail-list a:hover { color: var(--brand); border-color: var(--brand); }
.detail-list .big {
  font-family: var(--font-mono);
  font-size: clamp(1.05rem, 2.2vw, 1.3rem);
  font-weight: 400;
  line-height: 1.4;
}
.detail-list a.big { display: inline-flex; align-items: center; min-height: 44px; }

/* Map. Loads immediately — client decision, see contact.html. */
.map-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--mist);
}
@media (min-width: 900px) { .map-frame { aspect-ratio: 21 / 11; } }
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.map-note { margin-top: var(--s-2); font-size: 0.95rem; color: var(--muted); }

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--hairline);
  padding-block: var(--s-5);
  font-size: var(--size-small);
}
.footer-grid {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
}
@media (min-width: 768px) { .footer-grid { grid-template-columns: 1.2fr 1fr 1fr; } }
.site-footer a { color: var(--ink); text-decoration: none; }
.site-footer a:hover { color: var(--brand); }
.site-footer ul { list-style: none; display: grid; }
.site-footer ul li { display: flex; align-items: center; min-height: 44px; }
.site-footer ul a { display: inline-flex; align-items: center; min-height: 44px; }
.footer-legal {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 0.95rem;
}

/* ---------- Decorative ring motif ---------- */

.rings {
  position: absolute;
  top: 0;
  left: 50%;
  width: min(900px, 130vw);
  height: min(900px, 130vw);
  transform: translate(-50%, -30%);
  pointer-events: none;
  z-index: -1;
}
.rings circle { fill: none; stroke: var(--mist); stroke-width: 1; }
.rings .accent { stroke: var(--brand); opacity: 0.12; }
@media (prefers-reduced-motion: no-preference) {
  .rings { animation: drift 90s linear infinite; }
}
@keyframes drift {
  from { transform: translate(-50%, -30%) rotate(0deg); }
  to   { transform: translate(-50%, -30%) rotate(360deg); }
}
/* The motif is deliberately larger than the viewport. Clip it here rather than
   leaning on body { overflow-x: hidden }, which would also hide real bugs. */
.has-rings { position: relative; isolation: isolate; overflow: hidden; }

/* ---------- Entrance ---------- */

@media (prefers-reduced-motion: no-preference) {
  .rise { opacity: 0; animation: rise 0.9s var(--ease) forwards; }
  .rise-2 { animation-delay: 0.1s; }
  .rise-3 { animation-delay: 0.2s; }
  .rise-4 { animation-delay: 0.3s; }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Reduced motion must still land on the final state — content is never left
   invisible. .rise only sets opacity: 0 inside the no-preference query above,
   so there is nothing to undo here beyond killing durations. */
@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;
  }
  html { scroll-behavior: auto; }
}

/* ---------- Contact form ---------- */

.form-layout {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: start;
}
@media (min-width: 900px) { .form-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); } }

.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.field { margin-bottom: var(--s-3); }
.field label {
  display: block;
  font-size: var(--size-small);
  font-weight: 500;
  margin-bottom: var(--s-1);
}
.field input,
.field textarea {
  width: 100%;
  min-height: 44px;
  padding: 0.65rem 0.85rem;
  font-family: inherit;
  font-size: var(--size-body);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.field textarea { resize: vertical; line-height: 1.6; }
.field input:hover,
.field textarea:hover { border-color: var(--muted); }
.field input:focus,
.field textarea:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--mist);
  outline: none;
}
/* :focus-visible still paints the shared ring for keyboard users. */
.field input:focus-visible,
.field textarea:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }

.form-error {
  margin-bottom: var(--s-3);
  padding: var(--s-2);
  border-left: 3px solid var(--brand);
  border-radius: 0;
  background: var(--mist);
  color: var(--ink);
  font-size: var(--size-small);
}

.form-sent {
  padding: var(--s-4);
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  background: var(--paper);
}
.form-sent h3 { margin-bottom: var(--s-2); }
.form-sent p { color: var(--muted); }

/* ---------- Category imagery ---------- */

/* All eleven tiles are now Canbell's own product photography, cropped to a
   shared 4:3 so the set reads as one consistent line regardless of source
   framing. */
.cat-art {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  margin-bottom: var(--s-3);
  border-radius: 6px;
  background: var(--mist);
  border: 1px solid var(--hairline);
}
