/* ============================================================================
   base.css
   Shared by every page, without change.
   This file reads SEMANTIC TOKENS ONLY. No primitive, no raw colour, no raw
   size, no raw duration appears below this line. If something needed here has
   no semantic token, the answer is to add the token, not to hardcode it.

   Everything that differs between the three directions differs because a token
   resolves differently. That is the point of the exercise: identical markup,
   identical stylesheet, one swapped import.
   ========================================================================= */

/* ==========================================================================
   1. RESET
   ========================================================================= */

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

/*
  The user agent styles [hidden] as display:none, but at (0,1,0) specificity,
  which any class selector ties and any later class selector beats. So the
  moment a component sets a display value on itself, its own hidden attribute
  stops working and it renders for every visitor with JavaScript off. That is
  not hypothetical: .switcher { display: flex } did exactly this, and the
  control shipped visible and inert to anyone without JavaScript.

  Global rather than scoped to the component that hit it, because the next
  component to set a display value will hit it too. !important rather than
  higher specificity, because the point is that no author rule should win.
*/
[hidden] { display: none !important; }

/*
  scrollbar-gutter reserves the scrollbar's width whether or not the page is
  long enough to scroll. Without it, a short page gets the full viewport and a
  long one loses 15px to its scrollbar, so everything centred moves sideways by
  half that when you navigate from one to the other. It showed in the header
  first because the header is the thing that stays put between pages. Overlay
  scrollbars take no width, so on those this does nothing.
*/
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body,
h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
  padding: 0;
}

ul, ol { list-style: none; }

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  cursor: pointer;
}

::selection {
  background-color: var(--surface-selection);
  color: var(--text-selection);
}

/* ==========================================================================
   2. PAGE
   ========================================================================= */

body {
  background-color: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--type-body-font);
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  line-height: var(--type-body-leading);
  letter-spacing: var(--type-body-tracking);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  transition:
    background-color var(--motion-duration-ui) var(--motion-ease-ui),
    color            var(--motion-duration-ui) var(--motion-ease-ui);
}

/* ==========================================================================
   3. FOCUS
   Never removed, never suppressed. One rule, applied everywhere.
   ========================================================================= */

:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-focus);
}

/* ==========================================================================
   4. SKIP LINK
   ========================================================================= */

.skip-link {
  position: absolute;
  left: var(--space-inset-sm);
  top: var(--space-inset-sm);
  z-index: var(--layer-overlay);
  transform: translateY(-200%);
  padding: var(--space-inset-xs) var(--space-inset-md);
  background-color: var(--surface-accent);
  color: var(--text-on-accent);
  border-radius: var(--radius-control);
  box-shadow: var(--elevation-overlay);
  font-family: var(--type-ui-font);
  font-size: var(--type-ui-size);
  font-weight: var(--type-ui-weight);
  line-height: var(--type-ui-leading);
  text-decoration: none;
  transition: transform var(--motion-duration-ui) var(--motion-ease-ui);
}

.skip-link:focus { transform: translateY(0); }

/* ==========================================================================
   5. SITE HEADER
   ========================================================================= */

.site-header {
  border-bottom: var(--border-width-hairline) solid var(--divider-color);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline-sm);
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-inset-sm);
}

.site-header__name {
  font-family: var(--type-ui-font);
  font-size: var(--type-ui-size);
  font-weight: var(--type-ui-weight);
  line-height: var(--type-ui-leading);
  letter-spacing: var(--type-ui-tracking);
  color: var(--text-primary);
  text-decoration: none;
}

.site-header__name:hover { color: var(--text-link-hover); }

/* Two links. The site name beside them is the third, so the nav itself stays
   at two and anything deeper is reached from the page it belongs to.

   The current page is marked with aria-current, and the rule below hangs off
   that attribute rather than off a class the markup has to remember to set.
   One source of truth, and the one screen readers already read. */
.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline-md);
}

.site-nav__link {
  font-family: var(--type-ui-font);
  font-size: var(--type-ui-size);
  font-weight: var(--type-ui-weight);
  line-height: var(--type-ui-leading);
  letter-spacing: var(--type-ui-tracking);
  color: var(--text-muted);
  text-decoration: none;
}

.site-nav__link:hover { color: var(--text-link-hover); }

/* Colour is not the only carrier: the current page also gets the underline. */
.site-nav__link[aria-current="page"] {
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: var(--space-inline-2xs);
}

/* ==========================================================================
   6. SWITCHER
   Hidden until script unhides it, so a no-JS visitor is never shown a control
   that cannot work. The system preferences still apply without JS.

   Two triggers, one per question, each a native <select> styled to match Figma
   node 68:3440: a leading icon, the current value as the label, and a trailing
   chevron, filled with the theme accent. The select is the trigger itself
   rather than a wrapper around one, so the whole pill is the hit target and
   the label is the select's own rendering of its selected option: one source
   of truth, nothing for script to keep in sync.

   The popup stays the browser's own. That is the entire reason for using a
   select: keyboard, screen reader, touch and mobile behaviour are correct
   without a line of code, and a custom listbox would have to earn each of them
   back.
   ========================================================================= */

.switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--switcher-gap);
}

.switcher__control {
  position: relative;
  display: inline-flex;
  /* The icons inherit this, which is the whole trick: --text-on-accent is
     white in light and light-hc and near-black in dark and dark-hc, and an SVG
     stroked with currentColor follows it. A colour baked into the file, or an
     SVG used as a background-image, would be wrong in half the states. */
  color: var(--text-on-accent);
}

.switcher__select {
  appearance: none;
  inline-size: 100%;
  /* Room for the icon on one side and the chevron on the other, so the label
     lands where the design puts it and the select still covers the whole pill. */
  padding-block: var(--switcher-inset-block);
  padding-inline-start:
    calc(var(--switcher-inset-inline) + var(--switcher-icon-box) + var(--switcher-gap));
  padding-inline-end:
    calc(var(--switcher-inset-inline) + var(--switcher-chevron-size) + var(--switcher-gap));
  border: var(--border-width-none);
  border-radius: var(--switcher-radius);
  background-color: var(--surface-accent);
  color: var(--text-on-accent);
  font-family: var(--switcher-label-font);
  font-size: var(--switcher-label-size);
  font-weight: var(--switcher-label-weight);
  /* Matches the icon box, which is what makes the control 40px tall without a
     height being set anywhere: 8 + 24 + 8. The box, not the glyph inside it,
     so the glyph can be resized without moving the control's height. */
  line-height: var(--switcher-icon-box);
  text-overflow: ellipsis;
  cursor: pointer;
}

/* The longest label sets the width, so the header does not move when the
   selection changes. A min-width rather than a truncated label. */
.switcher__select--mode { min-inline-size: var(--switcher-mode-min-width); }

.switcher__select:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* Both sit over the select and pass clicks through to it, so the pill is one
   target rather than three. */
.switcher__icon,
.switcher__chevron {
  position: absolute;
  inset-block-start: 50%;
  translate: 0 -50%;
  pointer-events: none;
}

.switcher__icon {
  inset-inline-start: var(--switcher-inset-inline);
  /* The box holds the space. The glyph is smaller than the box and is centred
     in it, so shrinking the glyph moves nothing else in the control. */
  display: grid;
  place-items: center;
  inline-size: var(--switcher-icon-box);
  block-size: var(--switcher-icon-box);
}

.switcher__chevron {
  inset-inline-end: var(--switcher-inset-inline);
  inline-size: var(--switcher-chevron-size);
  block-size: var(--switcher-chevron-size);
}

.switcher__icon svg {
  inline-size: var(--switcher-icon-glyph);
  block-size: var(--switcher-icon-glyph);
}

.switcher__chevron svg {
  inline-size: 100%;
  block-size: 100%;
}

/*
  Stroke width in rendered pixels rather than in glyph units.

  These icons are drawn on a 24 unit viewBox, so a stroke-width attribute is
  scaled by whatever size the glyph is rendered at: the 2 in the markup renders
  at 2px in a 24px box and would render at 1.67px in a 20px one. Chasing the
  wanted width by tuning the attribute would leave a number in the markup that
  silently depends on a token in the stylesheet.

  non-scaling-stroke takes the stroke out of the scaled coordinate system, so
  the width below is the width on screen whatever the glyph size becomes. That
  is the same separation the icon box and glyph tokens make, applied to the
  third property that was quietly coupled to glyph size.
*/
.switcher__icon svg path {
  vector-effect: non-scaling-stroke;
  stroke-width: var(--switcher-icon-stroke);
}

/*
  The mode icon follows the selection. Declarative, so script sets one
  attribute rather than rewriting markup.

  visibility rather than display, because display is already carrying the
  centring above: these selectors outrank .switcher__icon, so toggling display
  here silently replaced grid with block and dropped the glyph to the top of
  its box. All three icons are absolutely positioned in the same place, so
  hiding them costs no layout, and they are aria-hidden either way.
*/
.switcher__control [data-icon] { visibility: hidden; }
.switcher__control[data-selected="light"] [data-icon="light"],
.switcher__control[data-selected="dark"]  [data-icon="dark"],
.switcher__control[data-selected="hc"]    [data-icon="hc"] { visibility: visible; }

/* ==========================================================================
   6b. VISUALLY HIDDEN
   For content that must stay in the accessibility tree while leaving no mark
   on the page. Not display:none and not visibility:hidden, either of which
   would take it out of the tree and defeat the point.
   ========================================================================= */

.visually-hidden {
  position: absolute;
  inline-size: var(--border-width-hairline);
  block-size: var(--border-width-hairline);
  margin: calc(var(--border-width-hairline) * -1);
  padding: var(--space-none);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: var(--border-width-none);
}

/* ==========================================================================
   7. CASE STUDY HEADER
   ========================================================================= */

.case-study { padding-block-end: var(--space-section); }

.case-study__header {
  padding-block: var(--space-section-tight) var(--space-stack-xl);
}

.eyebrow {
  font-family: var(--type-eyebrow-font);
  font-size: var(--type-eyebrow-size);
  font-weight: var(--type-eyebrow-weight);
  line-height: var(--type-eyebrow-leading);
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: var(--type-eyebrow-transform);
  color: var(--text-accent);
  margin-block-end: var(--space-stack-sm);
}

.case-study__title {
  font-family: var(--type-display-font);
  font-size: var(--type-display-size);
  font-weight: var(--type-display-weight);
  line-height: var(--type-display-leading);
  letter-spacing: var(--type-display-tracking);
  text-transform: var(--type-display-transform);
  color: var(--text-primary);
  max-width: var(--measure-display);
  text-wrap: balance;
}

/* Home page h1 override. A page title that is a sentence rather than a name.

   Before: the home h1 used .case-study__title alone, the hero style, which
   reads --type-display-*. Now: the same class plus this modifier, which swaps
   in --type-headline-* and changes nothing else about the title.

   Why: at the hero size in Sand the h1 set at 96px over four lines, a 794px
   hero, putting the Work heading at 947px and below the fold at 1280x900.

   Re-measured after the career paragraph moved out of the hero into the
   Career section, leaving one lede. At the hero size the h1 is still 96px
   over four lines, a 638px hero, and the Work heading runs 791 to 834px: it
   clears 1280x900 but is cut at 1280x800, the viewport that matters. At the
   headline size it is 64px over three lines, a 435px hero, with the Work
   heading at 588 to 631px, clearing 1280x800 by 169px. So the headline stays.

   Reverting is a one-line change: in index.html, delete
   "case-study__title--headline" from the h1's class attribute, the line that
   reads <h1 class="case-study__title case-study__title--headline">. The h1
   then takes the hero style again. This rule and the headline tokens can
   stay; nothing else reads them.

   The hero style itself, .case-study__title above, is untouched. About, Work,
   Peers and all three case study titles use it without this modifier. */
.case-study__title--headline {
  font-family: var(--type-headline-font);
  font-size: var(--type-headline-size);
  font-weight: var(--type-headline-weight);
  line-height: var(--type-headline-leading);
  letter-spacing: var(--type-headline-tracking);
  text-transform: var(--type-headline-transform);
}

.case-study__lede {
  margin-block-start: var(--space-stack-md);
  font-family: var(--type-lede-font);
  font-size: var(--type-lede-size);
  font-weight: var(--type-lede-weight);
  line-height: var(--type-lede-leading);
  letter-spacing: var(--type-lede-tracking);
  color: var(--text-secondary);
  max-width: var(--measure-narrow);
  text-wrap: pretty;
}

/* ---- meta panel: exercises surface, border, radius and elevation together,
        which is where the three directions separate most visibly ---- */

.case-study__meta {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-stack-md);
  margin-block-start: var(--space-stack-lg);
  padding: var(--space-inset-md);
  background-color: var(--surface-raised);
  border: var(--border-width-default) solid var(--border-container);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-raised);
}

@media (min-width: 34rem) {
  .case-study__meta {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-inline-md);
  }
}

.case-study__meta dt {
  font-family: var(--type-label-font);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-leading);
  letter-spacing: var(--type-label-tracking);
  text-transform: var(--type-label-transform);
  color: var(--text-muted);
  margin-block-end: var(--space-stack-2xs);
}

.case-study__meta dd {
  font-family: var(--type-caption-font);
  font-size: var(--type-caption-size);
  font-weight: var(--type-caption-weight);
  line-height: var(--type-caption-leading);
  letter-spacing: var(--type-caption-tracking);
  color: var(--text-primary);
}

/* ==========================================================================
   8. SITE FOOTER
   ========================================================================= */

.site-footer {
  border-block-start: var(--border-width-hairline) solid var(--divider-color);
  background-color: var(--surface-subtle);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline-sm) var(--space-inline-lg);
  align-items: baseline;
  justify-content: space-between;
  padding-block: var(--space-inset-lg);
}

.site-footer p,
.site-footer li {
  font-family: var(--type-caption-font);
  font-size: var(--type-caption-size);
  font-weight: var(--type-caption-weight);
  line-height: var(--type-caption-leading);
  letter-spacing: var(--type-caption-tracking);
  color: var(--text-muted);
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline-md);
}

.site-footer a {
  color: var(--text-link);
  text-decoration-line: underline;
  text-decoration-thickness: var(--border-width-hairline);
  text-underline-offset: var(--space-stack-2xs);
  transition: color var(--motion-duration-ui) var(--motion-ease-ui);
}

.site-footer a:hover { color: var(--text-link-hover); }
