/* ==========================================================================
   Components added after the first audit: the architecture diagram's palette,
   the layouts that were leaving half a desktop window empty, the spacing
   utilities that replaced thirty-two inline style="" attributes, and the
   print rules.

   Imported alongside base.css in Base.astro. Vite merges the two into one
   stylesheet per page, so this costs no extra request.
   ========================================================================== */

/* -- Spacing utilities ----------------------------------------------------- */
/*
   A five-step vertical scale, replacing the inline style="" rules the
   templates used to carry. Two reasons that mattered beyond tidiness: those
   declarations were the only thing keeping 'unsafe-inline' in the style-src
   directive of public/_headers, and they had drifted into ten near-identical
   values (0.6 / 1 / 1.2 / 1.4 / 1.6 / 1.8 / 2.4 / 2.6 / 3 / 3.4rem) that no
   longer described a rhythm.
*/
.mt-1 { margin-top: 0.6rem; }
.mt-2 { margin-top: 1.2rem; }
.mt-3 { margin-top: 1.8rem; }
.mt-4 { margin-top: 2.6rem; }
.mt-5 { margin-top: 3.4rem; }

/* -- Page titles ----------------------------------------------------------- */
/*
   Every page needs exactly one h1. Four of them — about, work, cv, contact —
   used to open on an h2, which reads to a screen reader and to a crawler as a
   document with no title at all. The h1 default is hero-sized, so those pages
   take the h2 setting through a class rather than by picking a smaller tag.
*/
.page-title {
  font-size: var(--fs-h2);
  line-height: 0.94;
  letter-spacing: -0.025em;
}

/* -- Narrow reading column ------------------------------------------------- */
/*
   Case studies are a single column of prose. Left-aligned inside the full
   1240px container they left the right-hand 40% of a desktop window empty,
   which reads as a layout that failed rather than a column that was chosen.
*/
.wrap--narrow { max-width: 880px; }

/* -- Architecture diagram -------------------------------------------------- */
/*
   Colours here are tokens for a reason: fills written as hex inside the SVG
   are invisible to tests/contrast.spec.ts, and that is how the first version
   of this diagram shipped label text at 3.20:1. Sizes are user units against
   a 320x400 viewBox — the smallest, 11, lands at roughly 11px on a phone.
*/
.diagram {
  width: 100%;
  height: auto;
  /* Caps how tall the artefact card grows beside the hero copy on a desktop.
     preserveAspectRatio then centres it rather than distorting it. */
  max-height: 440px;
  margin-inline: auto;
}

.diagram text {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
}

.dg-box { fill: var(--diagram-ground); stroke: var(--diagram-line); stroke-width: 1.5; }
.dg-box-bridge { fill: var(--diagram-ground-bridge); stroke: var(--brass); }

.dg-t1 { fill: var(--diagram-text); font-size: 15px; letter-spacing: 0.1em; }
.dg-t2 { fill: var(--diagram-text-dim); }
.dg-b1 { fill: var(--brass); font-size: 16px; letter-spacing: 0.1em; }
.dg-b2 { fill: var(--brass-mid); }

.dg-flow-out { stroke: var(--brass); stroke-width: 1.5; fill: none; }
.dg-flow-back { stroke: var(--diagram-text-dim); stroke-width: 1.5; fill: none; }
.dg-arrow-out { fill: var(--brass); }
.dg-arrow-back { fill: var(--diagram-text-dim); }
.dg-label-out { fill: var(--brass-mid); letter-spacing: 0; }
.dg-label-back { fill: var(--diagram-text-dim); letter-spacing: 0; }
.dg-foot { fill: var(--diagram-text-dim); letter-spacing: 0.14em; }

/* -- About: the mark in the portrait slot ---------------------------------- */
/*
   Not scoped to about.astro, deliberately. Astro stamps its scope attribute
   onto elements in the component that declares the style, and this class
   lands on the <svg> inside Mark.astro — so the scoped rule compiled to
   `.about-mark[data-astro-cid-…]`, matched nothing, and the mark rendered at
   full width in inherited cream instead of 240px in brass.
*/
.artefact--portrait { align-items: center; padding: 3rem; }

.about-mark {
  width: min(240px, 60%);
  color: var(--brass);
}

.hero-grid--top { align-items: start; }

/* -- CV -------------------------------------------------------------------- */

.cv-contact {
  font-size: 0.8rem;
  letter-spacing: 0.06em;
}

/* -- Case study ------------------------------------------------------------ */

.rows--case { max-width: 44rem; }

/*
   The end of a case study used to be a single "All work" link. Someone who
   has just read the strongest argument on the site had nowhere to go but
   backwards.
*/
.case-end {
  margin-top: 3.4rem;
  padding-top: 2.4rem;
  border-block-start: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
  align-items: center;
}

.case-next {
  margin-top: 2.4rem;
  display: grid;
  gap: 0.4rem;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--line);
  padding: 1.4rem 1.6rem;
  transition: border-color var(--dur-fast) var(--ease);
}
.case-next:hover { border-color: var(--brass); color: inherit; }
.case-next:active { transform: translateY(1px); }

.case-next-label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--steel);
}
.case-next-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* -- Contact --------------------------------------------------------------- */
/*
   Two columns from 900px up. The form on its own occupied a 34rem strip of a
   1440px window and left the rest blank; the aside puts the answers a person
   wants before they type — what happens next, how long it takes, what not to
   send — where they will actually read them.
*/
.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
  margin-top: 2.6rem;
}

.contact-aside {
  border-inline-start: 1px solid var(--line);
  padding-inline-start: clamp(1.25rem, 3vw, 2.4rem);
}

@media (max-width: 900px) {
  .contact-grid { grid-template-columns: 1fr; }
  .contact-aside {
    border-inline-start: 0;
    border-block-start: 1px solid var(--line);
    padding-inline-start: 0;
    padding-block-start: 2rem;
  }
}

.contact-aside dl { margin: 0; }
.contact-aside dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--steel);
  margin-block-start: 1.8rem;
}
.contact-aside dt:first-of-type { margin-block-start: 0; }
.contact-aside dd {
  margin: 0.5rem 0 0;
  color: var(--text-dim);
  font-size: var(--fs-small);
  max-width: 42ch;
}

/* Same fix as the footer: two links stacked by a <br> measured 15px tall with
   23px between their centres, which fails 2.5.8 on size and is too close
   together for its spacing exception. */
.contact-aside dd a {
  display: inline-block;
  padding-block: 0.3rem;
}

/* The form no longer needs its own max-width: the grid column supplies one. */
.contact-grid .form { max-width: none; margin-top: 0; }

/* Marks the one field that is not required, rather than leaving a visitor to
   discover it by submitting. */
.field-optional {
  color: var(--steel);
  letter-spacing: 0.06em;
  text-transform: none;
}

/* -- Footer legal ---------------------------------------------------------- */

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0.6rem 0 0;
  padding: 0;
  list-style: none;
}

/* -- Root hand-off and 404 ------------------------------------------------- */

.handoff {
  padding: 3rem;
  font-family: var(--font-body);
}

.notfound { padding-block: clamp(4rem, 12vw, 9rem); }
.notfound-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
  margin-top: 2.6rem;
}

/* -- Print ----------------------------------------------------------------- */
/*
   The content-visible-first rule protects the case where JavaScript never
   runs. It does not protect the case where JavaScript runs and the reveal has
   not fired, because nothing below the fold has been scrolled into view —
   which is exactly what printing, and every tool that captures a full page,
   asks the browser to do. Without this block a printed copy of the homepage
   loses the services, the work cards and the statement break entirely. It was
   found by screenshotting the built site, not by reading the CSS.
*/
@media print {
  [data-motion='on'] .reveal,
  [data-motion='on'] .reveal-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  body { background: #fff; color: #000; }

  .site-header,
  .skip-link,
  .hero-actions,
  .case-end,
  .case-next,
  .notfound-links,
  .form { display: none; }

  .section,
  .hero { padding-block: 1.5rem; }

  a { color: #000; }
  .diagram { max-height: 320px; }
}

/* -- Form messages --------------------------------------------------------- */
/*
   Inline, per field, wired with aria-describedby. A browser's own validation
   bubble is announced once, points at nothing a screen reader can navigate
   back to, and vanishes on the next keystroke; these stay until the field is
   fixed. public/js/contact.js sets form.noValidate at runtime so that losing
   the script costs the better messages and not the checking itself.

   :empty keeps the box out of the layout until it has something to say, so a
   valid form has no reserved gaps in it.
*/
.field-error {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: var(--warn);
}
.field-error:empty { display: none; }

.field-hint {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: var(--steel);
}

/* Colour is not the only signal: an invalid field also gains a rule down its
   inline start, which survives a monochrome display and colour blindness. */
.field input[aria-invalid='true'],
.field textarea[aria-invalid='true'] {
  border-color: var(--warn);
  border-inline-start-width: 3px;
}

/* The CV's contact row is a line of links, not a sentence containing them, so
   it does not lean on 2.5.8's inline exception the way a link inside a
   paragraph does. The padding costs nothing and the row is what someone taps
   when they have decided to make contact. */
.cv-contact a {
  display: inline-block;
  padding-block: 0.3rem;
}

/* -- WordPress admin bar --------------------------------------------------- */
/*
   Only a logged-in editor ever sees this. The admin bar is fixed at the top of
   the window, so a sticky header with top: 0 slides underneath it. The heights
   are core's own: 32px on desktop, 46px below its 782px breakpoint, where it
   also stops being fixed and scrolls away with the page.
*/
.admin-bar .site-header { top: 32px; }

@media screen and (max-width: 782px) {
  .admin-bar .site-header { top: 46px; }
}

@media screen and (max-width: 600px) {
  .admin-bar .site-header { top: 0; }
}
