/* ==========================================================================
   dericktw.online
   One stylesheet, mobile first. Design tokens follow the channel banner:
   charcoal, cream, one orange accent, Poppins headings, Inter body.
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------
   The @font-face rules are not here. includes/header.php emits them inline,
   but only for font files that are actually present in /assets/fonts, so a
   font you have not added yet costs zero requests instead of a 404 on every
   visit. Until the files are there the system stack below carries the site.
   See assets/fonts/README.md.
   ------------------------------------------------------------------------ */

/* --- Tokens --------------------------------------------------------------- */

:root {
  --focus: #b5431f;       /* see :focus-visible — 4.97:1 on cream, 5.54:1 on white */
  --charcoal: #1c1c1c;
  --charcoal-deep: #141414;
  --cream: #f6f2ec;
  --white: #ffffff;

  /* Brand orange, used for accents on dark backgrounds. */
  --orange: #e8643c;
  /* Deeper orange for button fills and links on cream: white text on this
     clears WCAG AA, which #e8643c does not. Same family, same feel. */
  --orange-ink: #b5431f;

  --grey-dark: #4a4a4a;   /* secondary text on cream */
  --grey-light: #c9c9c9;  /* secondary text on charcoal */
  --grey-mid: #8a8a8a;    /* fine print on the deep charcoal footer */
  --rule-dark: rgba(255, 255, 255, 0.14);
  --rule-light: rgba(28, 28, 28, 0.12);

  --font-head: "Poppins", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;

  --wrap: 1100px;
  --gutter: 1.25rem;
  --radius-pill: 999px;
  --radius-card: 14px;
}

/* --- Reset ---------------------------------------------------------------- */

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--charcoal);
  font-family: var(--font-body);
  font-size: 1.0625rem;         /* 17px */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

img { max-width: 100%; height: auto; display: block; }

ul { margin: 0; padding: 0; list-style: none; }

a { color: var(--orange-ink); }

code {
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.12);
}

/* The focus ring switches with the background it sits on. #e8643c scores
   only 2.98:1 against the cream page background, just under the 3:1 that
   WCAG 2.2 SC 1.4.11 requires of a focus indicator; --orange-ink clears it at
   4.97:1. On the dark sections the relationship inverts, so --orange returns
   there at 5.13:1. */
:focus-visible {
  outline: 3px solid var(--focus, var(--orange-ink));
  outline-offset: 3px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- Helpers -------------------------------------------------------------- */

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

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--orange-ink);
  color: var(--white);
  font-weight: 600;
}

.skip-link:focus { left: 0; }

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

.btn {
  display: inline-block;
  padding: 0.8rem 1.6rem;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.btn:active { transform: translateY(1px); }

.btn--primary {
  background: var(--orange-ink);
  color: var(--white);
}

.btn--primary:hover { background: #9c3818; }

.btn--ghost {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}

.section--dark .btn--ghost:hover,
.hero .btn--ghost:hover { background: rgba(255, 255, 255, 0.1); }

.section--cream .btn--ghost { color: var(--charcoal); }
.section--cream .btn--ghost:hover { background: rgba(28, 28, 28, 0.06); }

/* --- Header and navigation ------------------------------------------------ */

.site-header {
  background: var(--charcoal);
  color: var(--white);
  border-bottom: 1px solid var(--rule-dark);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 64px;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  color: var(--white);
  text-decoration: none;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.0625rem;
}

/* The banner separates name from role with a solid orange bar, not a pipe. */
.brand__divider {
  width: 3px;
  height: 1.1em;
  border-radius: 2px;
  background: var(--orange);
  text-indent: -9999px;
  overflow: hidden;
  align-self: center;
}

.brand__role {
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--grey-light);
  font-size: 0.95rem;
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* A flex item will shrink below its declared width; at 320px this button
     was rendering 22px wide, half the 44px target it claims. min-width is
     what actually holds the floor. */
  width: 44px; height: 44px;
  min-width: 44px; min-height: 44px;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: 1px solid var(--rule-dark);
  border-radius: 8px;
  color: var(--white);
  cursor: pointer;
}

.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 20px; height: 2px;
  background: currentColor;
  border-radius: 2px;
  content: "";
}

.nav-toggle__bars { position: relative; }
.nav-toggle__bars::before { position: absolute; top: -6px; }
.nav-toggle__bars::after  { position: absolute; top: 6px; }

.site-nav {
  display: none;
  width: 100%;
  order: 3;
}

.site-nav.is-open { display: block; }

.site-nav ul {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding-bottom: 0.75rem;
}

.site-nav a {
  display: block;
  padding: 0.6rem 0;
  color: var(--white);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-dark);
}

.site-nav a:hover { color: var(--orange); }

@media (min-width: 48em) {
  .nav-toggle { display: none; }

  .site-nav {
    display: block;
    width: auto;
    order: 0;
  }

  .site-nav ul {
    flex-direction: row;
    gap: 1.75rem;
    padding: 0;
  }

  .site-nav a {
    padding: 0;
    border: 0;
    font-size: 0.975rem;
  }
}

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

.hero {
  position: relative;
  isolation: isolate;          /* keeps the ::before behind the content only */
  overflow: hidden;            /* glows must not leak past the section edge */
  background: var(--charcoal);
  color: var(--white);
  padding-block: 3rem 3.5rem;
}

/* Every variant paints into this one layer. It sits behind the content and
   is purely decorative, so it is never in the accessibility tree and never
   intercepts a click. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.hero__inner {
  position: relative;
  display: grid;
  gap: 2.5rem;
}

.hero__eyebrow {
  margin: 0 0 1rem;
  color: var(--orange);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hero__title {
  font-size: clamp(2rem, 7vw, 3.25rem);
  margin-bottom: 1rem;
  max-width: 16ch;
}

.hero__lead {
  color: var(--grey-light);
  font-size: 1.125rem;
  max-width: 48ch;
  margin-bottom: 1.75rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.75rem;
}

/* "New videos weekly" — the badge from the banner. */
.badge {
  display: inline-block;
  padding: 0.45rem 1.1rem;
  border-radius: var(--radius-pill);
  background: var(--orange-ink);
  color: var(--white);
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
}

.hero__portrait { justify-self: start; }

/* The brand portrait already sits on an orange disc, so no ring is added —
   the radius just clips the square corners away. */
.hero__portrait img {
  width: min(260px, 70vw);
  height: auto;
  border-radius: 50%;
  background: var(--charcoal-deep);
}

@media (min-width: 56em) {
  .hero { padding-block: 5rem 5.5rem; }

  .hero__inner {
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 4rem;
  }

  .hero__portrait img { width: 320px; }
}

/* --- Chips ---------------------------------------------------------------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.chip {
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--rule-dark);
  border-radius: var(--radius-pill);
  font-size: 0.85rem;
  color: var(--grey-light);
}

.section--cream .chip {
  border-color: var(--rule-light);
  color: var(--grey-dark);
}

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

.section { padding-block: 3.5rem; }

.section--flush { padding-top: 0; }

.section--cream {
  background: var(--cream);
  color: var(--charcoal);
}

.section--dark {
  background: var(--charcoal);
  color: var(--white);
}

.section--dark,
.site-header,
.site-footer { --focus: #e8643c; }   /* 5.13:1 on charcoal, 5.54:1 on the footer */

/* Links in a dark section take the accent colour — but a .btn is not that
   kind of link. This selector is (0,1,1) and .btn--primary is only (0,1,0),
   so without the :not() the rule repainted button labels orange on an orange
   fill: 1.67:1, against the 4.5 they need. The exclusion is what keeps the
   white label white. */
.section--dark a:not(.btn) { color: var(--orange); }

.section__title {
  font-size: clamp(1.6rem, 4.5vw, 2.25rem);
  margin-bottom: 0.75rem;
}

.section__title::after {
  display: block;
  width: 48px;
  height: 4px;
  margin-top: 0.85rem;
  border-radius: 2px;
  background: var(--orange);
  content: "";
}

.section__intro {
  max-width: 60ch;
  margin-bottom: 2rem;
  color: var(--grey-dark);
}

.section--dark .section__intro { color: var(--grey-light); }

.section__narrow { max-width: 42rem; }

.section__cta { margin-top: 2rem; }

.lead {
  font-size: 1.1875rem;
  line-height: 1.55;
}

@media (min-width: 56em) {
  .section { padding-block: 5rem; }
  .section--flush { padding-top: 0; }
}

/* --- Pillars -------------------------------------------------------------- */

.pillars {
  display: grid;
  gap: 1rem;
}

.pillar {
  padding: 1.5rem;
  background: var(--white);
  border: 1px solid var(--rule-light);
  border-radius: var(--radius-card);
}

.pillar__title {
  font-size: 1.1875rem;
  margin-bottom: 0.5rem;
}

/* The orange dot that used to sit here was decoration standing in for an
   icon. Now that each pillar has a real glyph, the dot would be noise. */
.pillar__title {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.pillar__icon {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  color: var(--orange-ink);   /* 4.97:1 on white — a UI glyph needs 3:1 */
}

.pillar p { font-size: 1rem; }

.pillar__tools {
  margin-top: 0.85rem;
  color: var(--grey-dark);
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}

@media (min-width: 40em) {
  .pillars { grid-template-columns: repeat(2, 1fr); }

  /* There are seven pillars, so the last one would otherwise be orphaned on a
     row of its own at both two and three columns. AI genuinely cuts across the
     other six rather than sitting beside them, so let it span the full row and
     read as deliberate. */
  .pillar--wide { grid-column: 1 / -1; }
}

@media (min-width: 60em) {
  .pillars { grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
}

/* --- Cards (trainings) ---------------------------------------------------- */

.cards {
  display: grid;
  gap: 1rem;
}

.card {
  padding: 1.75rem;
  background: var(--white);
  border: 1px solid var(--rule-light);
  border-top: 4px solid var(--orange);
  border-radius: var(--radius-card);
}

.card__title { font-size: 1.1875rem; }
.card p { font-size: 1rem; margin-bottom: 0; }

/* Between 40em and 52em the pillars and the video grid are already two
   columns, so leaving the cards stacked wasted most of a tablet's width.
   Three cards over two columns would strand the third, so it spans the row —
   the same move the AI pillar makes, for the same reason. */
@media (min-width: 40em) {
  .cards { grid-template-columns: repeat(2, 1fr); }
  .cards .card:last-child { grid-column: 1 / -1; }
}

@media (min-width: 52em) {
  .cards { grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
  .cards .card:last-child { grid-column: auto; }
}

/* --- Videos --------------------------------------------------------------- */

.videos {
  display: grid;
  gap: 1.75rem;
}

.video__thumb {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 10px;
  overflow: hidden;
  background: var(--charcoal-deep);
  cursor: pointer;
  aspect-ratio: 16 / 9;
}

.video__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.25s ease, opacity 0.2s ease;
}

.video__thumb:hover img { transform: scale(1.04); opacity: 0.85; }

.video__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 62px; height: 44px;
  border-radius: 10px;
  background: var(--orange-ink);
}

.video__play::after {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0; height: 0;
  border-style: solid;
  border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent #fff;
  content: "";
}

/* The real player replaces the button once clicked. */
.video__frame {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: 10px;
  display: block;
}

.video__title {
  font-size: 1.0625rem;
  margin: 0.9rem 0 0.25rem;
  line-height: 1.35;
}

.video__title a {
  color: var(--white);
  text-decoration: none;
}

.video__title a:hover {
  color: var(--orange);
  text-decoration: underline;
}

.video__date {
  margin: 0;
  color: var(--grey-light);
  font-size: 0.85rem;
}

@media (min-width: 40em) {
  .videos { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64em) {
  .videos { grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
}

/* --- Notices -------------------------------------------------------------- */

.notice {
  padding: 1rem 1.25rem;
  border-radius: 10px;
  border-left: 4px solid var(--orange);
  background: rgba(255, 255, 255, 0.07);
  font-size: 1rem;
}

.notice--dark   { color: var(--grey-light); }

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

.site-footer {
  background: var(--charcoal-deep);
  color: var(--grey-light);
  padding-block: 2.5rem;
  font-size: 0.95rem;
}

.site-footer a { color: var(--grey-light); }
.site-footer a:hover { color: var(--orange); }

.site-footer__brand {
  font-family: var(--font-head);
  font-weight: 700;
  color: var(--white);
  margin-bottom: 1rem;
}

.site-footer__brand span { color: var(--orange); }

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-bottom: 1.25rem;
}

.site-footer__fine {
  margin: 0;
  color: var(--grey-mid);
  font-size: 0.85rem;
}

@media (min-width: 48em) {
  .site-footer__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
  }

  .site-footer__brand,
  .site-footer__links { margin-bottom: 0; }

  .site-footer__fine { width: 100%; }
}

/* --- Print ---------------------------------------------------------------- */

@media print {
  .site-header, .site-nav, .video__thumb, .skip-link { display: none; }
  body { background: #fff; color: #000; }
}

/* --- Legal pages ---------------------------------------------------------- */

.legal h2 {
  margin-top: 2.25rem;
  font-size: 1.3rem;
}

.legal h3 {
  margin-top: 1.5rem;
  font-size: 1.0625rem;
}

.legal__review {
  padding: 1rem 1.25rem;
  margin-bottom: 1.75rem;
  border-left: 4px solid var(--orange-ink);
  border-radius: 10px;
  background: rgba(181, 67, 31, 0.07);
  font-size: 0.95rem;
}

.legal__list {
  margin: 0 0 1rem;
  padding-left: 1.1rem;
  list-style: disc;
}

.legal__list li { margin-bottom: 0.5rem; }

/* Tables are the one thing on this site wide enough to need their own
   scroller at 320px. Scrolling the table beats scrolling the page sideways. */
.legal__table {
  display: block;
  width: 100%;
  margin: 1rem 0 1.5rem;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.95rem;
}

.legal__table th,
.legal__table td {
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--rule-light);
  text-align: left;
  vertical-align: top;
}

.legal__table th { background: rgba(28, 28, 28, 0.04); }

.legal__identity {
  margin: 1rem 0 1.5rem;
  padding: 1.25rem;
  border: 1px solid var(--rule-light);
  border-radius: var(--radius-card);
  background: var(--white);
}

.legal__identity dt {
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--grey-dark);
}

.legal__identity dd {
  margin: 0.15rem 0 1rem;
}

.legal__identity dd:last-child { margin-bottom: 0; }

.legal__meta {
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule-light);
  color: var(--grey-dark);
  font-size: 0.9rem;
}

/* Long URLs and email addresses must never push the page sideways. */
.legal a { overflow-wrap: anywhere; }

/* --- Footer legal row ----------------------------------------------------- */

.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-bottom: 1.25rem;
  font-size: 0.9rem;
}

/* Every footer and nav link is a tap target, so give them the 44px the
   WCAG 2.2 target-size rule asks for without changing how they look. */
.site-footer__links a,
.site-footer__legal a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* --- 404 ------------------------------------------------------------------ */

.notfound__links {
  margin: 0 0 1.5rem;
  padding-left: 1.1rem;
  list-style: disc;
}

.notfound__links li { margin-bottom: 0.35rem; }

.notfound__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* --- Touch targets -------------------------------------------------------
   WCAG 2.2 SC 2.5.8 asks for 24x24 CSS px at AA; this site aims at the 44x44
   of SC 2.5.5 (AAA) for every standalone control, because the whole contact
   path is one-handed on a phone. Links inside a sentence are left alone:
   the spec exempts them, and padding them out would wreck the line spacing
   of the legal pages.
   ------------------------------------------------------------------------ */

.contact-direct a,
.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.site-footer__legal a {
  min-width: 44px;
  justify-content: center;
}

/* The brand is a navigation target, not prose. */
.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* ==========================================================================
   Motion
   --------------------------------------------------------------------------
   Three rules this section obeys.

   1. Nothing animates that costs layout. Only transform and opacity, so a
      reveal can never push text around and spoil CLS.
   2. Nothing is hidden unless JavaScript has already confirmed it can show
      it again. The .reveal states below only apply under html.js-reveal,
      a class main.js adds — and only after it has checked for
      IntersectionObserver support and for a reduced-motion preference.
      With JS off, every one of these elements renders normally.
   3. The reduced-motion block near the top of this file collapses every
      duration to 0.01ms. Each animation here uses `both` fill, so the final
      state still applies: the motion disappears, the content does not.
   ========================================================================== */

@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@keyframes rise-in-place {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: none; }
}

@keyframes draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* --- Hero entrance -------------------------------------------------------
   Pure CSS, so it runs on first paint with no script and no flash of
   hidden content. The hero is above the fold, which is exactly where a
   JS-driven reveal would be felt as a stutter. */

.hero__eyebrow,
.hero .chips,
.hero__actions {
  animation: rise 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.hero__eyebrow  { animation-delay: 0.05s; }
.hero .chips    { animation-delay: 0.15s; }
.hero__actions  { animation-delay: 0.25s; }

.hero__portrait img {
  animation: rise-in-place 0.7s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: 0.1s;
}

/* Each chip arrives just behind the one before it. Six steps is enough to
   read as a sequence; beyond that it starts to feel slow. */
.hero .chip {
  animation: rise 0.4s ease-out both;
}

.hero .chip:nth-child(1) { animation-delay: 0.20s; }
.hero .chip:nth-child(2) { animation-delay: 0.25s; }
.hero .chip:nth-child(3) { animation-delay: 0.30s; }
.hero .chip:nth-child(4) { animation-delay: 0.35s; }
.hero .chip:nth-child(5) { animation-delay: 0.40s; }
.hero .chip:nth-child(6) { animation-delay: 0.45s; }
.hero .chip:nth-child(7) { animation-delay: 0.50s; }
.hero .chip:nth-child(8) { animation-delay: 0.55s; }

/* --- Scroll reveal -------------------------------------------------------
   Guarded behind html.js-reveal. Read rule 2 above before touching this. */

.js-reveal .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.2, 0.7, 0.3, 1),
              transform 0.6s cubic-bezier(0.2, 0.7, 0.3, 1);
  transition-delay: var(--reveal-delay, 0s);
}

.js-reveal .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* The rule under a section heading draws itself in rather than appearing. */
.js-reveal .section__title.reveal::after {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s ease-out 0.2s;
}

.js-reveal .section__title.reveal.is-visible::after { transform: scaleX(1); }

/* --- Hover ---------------------------------------------------------------
   Cards lift a little and warm their border. Deliberately small: these are
   reading surfaces, not buttons, and a big movement on hover makes a page
   feel twitchy when the pointer crosses it on the way somewhere else. */

.pillar,
.card {
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.pillar:hover,
.card:hover {
  transform: translateY(-3px);
  border-color: rgba(181, 67, 31, 0.35);
  box-shadow: 0 10px 24px rgba(28, 28, 28, 0.07);
}

.pillar__icon { transition: transform 0.25s cubic-bezier(0.2, 0.7, 0.3, 1); }
.pillar:hover .pillar__icon { transform: scale(1.12) rotate(-4deg); }

.chip { transition: background-color 0.2s ease, border-color 0.2s ease; }

/* --- Header --------------------------------------------------------------
   A hairline and a shadow appear once the page has scrolled, so the bar
   separates from the content under it without being a heavy fixed bar at
   rest. */

.site-header {
  transition: box-shadow 0.25s ease, background-color 0.25s ease;
}

.site-header.is-scrolled {
  box-shadow: 0 1px 0 var(--rule-dark), 0 6px 18px rgba(0, 0, 0, 0.18);
}

/* --- Motion off ----------------------------------------------------------
   Belt and braces. The global block already zeroes durations; this makes
   the intent explicit for anything added here later, and guarantees the
   revealed state is the resting state. */

@media (prefers-reduced-motion: reduce) {
  .js-reveal .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .js-reveal .section__title.reveal::after { transform: scaleX(1); }

  .pillar:hover,
  .card:hover { transform: none; box-shadow: none; }

  .pillar:hover .pillar__icon { transform: none; }
}

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

.contact-note {
  max-width: 46ch;
  margin-top: 1.5rem;
  color: var(--grey-light);
  font-size: 1rem;
}

/* ==========================================================================
   Hero background
   --------------------------------------------------------------------------
   Graph paper, picking up the grid already in the channel banner, with a
   coral wash behind the portrait. Pure CSS: no image request, nothing for
   the Content-Security-Policy to allow, nothing extra to download.

   Contrast here was decided by measurement. The eyebrow is #e8643c, which
   scores 5.13:1 on bare charcoal — and a coral wash of only 10% behind it
   drops that to 4.56, while 30% reaches 3.31 and fails AA outright. Dimming
   the glow until the text was safe would have left a glow nobody could see.

   So the decoration is masked out of the text column instead: horizontally
   on desktop, and vertically once the hero stacks on a phone. Measured in
   the browser, the eyebrow ends at 18.1% of the hero width where the mask is
   still fully transparent, and on mobile every text row sits at mask alpha
   0.000. The furthest chip reaches 0.298, where grey chip text still scores
   better than 9:1.

   Three other variants — dot matrix, aurora and diagonal data streams — were
   built alongside this one and are in commit bc64958 if they are ever wanted
   back.
   ========================================================================== */

.hero::before {
  background-image:
    radial-gradient(58% 70% at 78% 44%, rgba(232, 100, 60, 0.26), transparent 70%),
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 100% 100%, 46px 46px, 46px 46px;
  -webkit-mask-image: linear-gradient(to right, transparent 0 46%, #000 84%);
          mask-image: linear-gradient(to right, transparent 0 46%, #000 84%);
}

/* The hero stacks below 56em: text on top, portrait underneath. The mask
   turns through 90 degrees to match, so the clear band still covers the
   words rather than the picture. */
@media (max-width: 56em) {
  .hero::before {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0 52%, #000 82%);
            mask-image: linear-gradient(to bottom, transparent 0 52%, #000 82%);
  }
}

/* A browser with no mask support would spread the grid across the whole
   hero, including under the eyebrow. Those browsers get plain charcoal
   instead: the design degrades, the contrast does not. */
@supports not ((-webkit-mask-image: linear-gradient(#000, #000)) or (mask-image: linear-gradient(#000, #000))) {
  .hero::before { background-image: none; }
}
