/* Shared chrome and type scale for the custom pages (About Me, case studies).
   Page-specific layout lives in that page's own stylesheet.
   Both palettes below are the single place to change the site's colour: every
   rule here and in the page stylesheets reads a token, so the dark block only
   has to restate the eight values rather than re-specify each rule. */

@font-face {
  font-family: "Play";
  src: url("fonts/Play-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Play";
  src: url("fonts/Play-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --page: #ffffff;
  --ink: rgb(16, 16, 17);
  --head: #000;    /* headings sit a shade below --ink in light */
  --body: rgb(97, 97, 97);
  --caption: rgb(115, 115, 115);
  --faint: rgb(155, 161, 165);
  --num: rgb(156, 156, 156);
  --accent: #5E81E2;
  --rule: rgba(0, 0, 0, 0.10);
  /* An arrow pointing up, for the cursor pill. Inline so it costs no request
     and cannot 404 the one place it is used. */
  --panel: rgb(240, 240, 244);   /* raised chrome (the theme button, the cursor pill) */

  --col: 1012px;   /* content column; each page sets its own */
  --gap: 32px;
}

/* Derived from the home page's :root[data-theme="dark"] in style.css so the
   two halves of the site are the same dark, not two near-misses: --page is its
   --bg, --ink its --t1, --body its --t2, --num its --t3, --accent its yellow.
   The in-between tones (--caption, --faint) keep the light palette's ordering —
   each step further from the reader is dimmer, which on dark means darker.

   The attribute is set on <html> by the inline block in every page's <head>,
   before the first paint. Nothing here waits on site.js. */
:root[data-theme="dark"] {
  --page: #121212;
  --ink: rgb(242, 242, 242);
  --head: #f2f2f2;
  --body: rgb(168, 168, 168);
  --caption: rgb(143, 143, 143);
  --faint: rgb(112, 116, 122);
  --num: rgb(107, 107, 107);
  --accent: #FFFA50;
  /* a touch stronger than light's 10%: a hairline of white on near-black
     carries less than the same fraction of black on white */
  --rule: rgba(255, 255, 255, 0.14);
  --panel: rgb(40, 40, 44);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: "Figtree", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; }

/* ---------- sticky top bar ---------- */

/* Pinned at the top; hides on the way down, comes back on the way up. */
#topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 63px;
  padding: 0 24px;
  background: var(--page);
  transition: transform 0.35s ease, opacity 0.35s ease;
}
#topbar.is-hidden {
  transform: translateY(-150px);
  opacity: 0;
}
/* once it is riding over the page rather than sitting above it, a hairline
   keeps the content from looking cut off as it scrolls underneath */
#topbar.is-floating {
  box-shadow: 0 1px 0 var(--rule);
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 18px;
  color: var(--caption);
  /* The bar now carries three children, and space-between alone would strand
     the tagline group in the middle. An auto margin eats the free space first,
     so the crumb keeps the left edge and the tagline group stays packed
     against the theme button at the right — including at the widths where the
     tagline is hidden and only the crumb and the button are left. */
  margin-right: auto;
}
.topbar-left a { color: inherit; text-decoration: none; }
.topbar-left a:hover { color: var(--accent); }
.topbar-left .sep { color: var(--caption); }

.topbar-right {
  display: flex;
  align-items: center;
  gap: 24px;
  font-family: "Play", sans-serif;
  font-size: 16px;
  color: var(--body);
  white-space: nowrap;
}
.topbar-right .sep-lg {
  font-family: "Figtree", sans-serif;
  font-size: 22px;
  color: var(--body);
}

/* ---------- theme toggle ---------- */

/* The same component as the home page's #theme-toggle (style.css): a clear
   36px square with a hairline edge, and the sun and moon on a wheel that
   rolls round on each press (site.js turns it). Keep the two in step.
   --ink-rgb is the home page's ink, scoped here because the case pages'
   palette has no rgb triple of its own. */
#theme-toggle {
  --ink-rgb: 40, 40, 40;
  position: relative;
  box-sizing: border-box;
  flex: none;
  display: block;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid rgba(var(--ink-rgb), 0.14);
  border-radius: 10px;
  background: transparent;
  color: rgba(var(--ink-rgb), 0.75);
  cursor: pointer;
  overflow: hidden;
  transition: background 200ms ease, color 200ms ease, border-color 200ms ease;
}
#theme-toggle:hover {
  color: rgba(var(--ink-rgb), 0.95);
  background: rgba(var(--ink-rgb), 0.05);
  border-color: rgba(var(--ink-rgb), 0.24);
}
:root[data-theme="dark"] #theme-toggle {
  --ink-rgb: 255, 255, 255;
  color: rgba(var(--ink-rgb), 0.88);
  border-color: rgba(var(--ink-rgb), 0.22);
}
:root[data-theme="dark"] #theme-toggle:hover {
  color: rgb(var(--ink-rgb));
  background: rgba(var(--ink-rgb), 0.09);
  border-color: rgba(var(--ink-rgb), 0.3);
}
/* :focus-visible only, so a mouse press does not leave a ring behind. The site
   hides the native cursor over buttons, which makes keyboard reachability the
   only way some readers will ever land on this. */
#theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The wheel. Its hub sits --r below the button's centre, so the face at the
   top of the rim is the one in the window and the other is hidden below.
   Which one is up is set from <html> before the first paint; site.js takes
   over the angle so each press keeps turning the same way. */
.theme-wheel {
  --r: 24px;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  transform-origin: 50% calc(50% + var(--r));
  transform: rotate(0deg);
}
:root[data-theme="dark"] .theme-wheel { transform: rotate(180deg); }
.theme-wheel.is-ready {
  transition: transform 0.6s cubic-bezier(0.45, 0.02, 0.2, 1);
}
.theme-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The far end of the rim, upside down so it arrives upright at the top. */
.theme-face:nth-child(2) {
  transform: translateY(calc(var(--r) * 2)) rotate(180deg);
}
.theme-sun,
.theme-moon {
  display: block;
  width: 15px;
  height: 15px;
}
@media (prefers-reduced-motion: reduce) {
  .theme-wheel.is-ready { transition: none; }
}

/* ---------- custom cursor ---------- */

#cursor-dot {
  position: fixed;
  left: 0;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 100px;
  /* Was a literal #000, which is a black dot on a #121212 page. --ink is the
     same value to within a rounding step in light, and is the readable one in
     dark, so the dot simply inverts with the page. */
  background: var(--ink);
  pointer-events: none;
  z-index: 100;
  opacity: 0;
  overflow: hidden;
  transition: width 0.24s cubic-bezier(.34, 1.15, .64, 1),
              height 0.24s cubic-bezier(.34, 1.15, .64, 1),
              margin 0.24s cubic-bezier(.34, 1.15, .64, 1),
              background-color 0.24s ease,
              box-shadow 0.24s ease;
}

/* Over anything carrying data-cursor the dot grows into a frosted pill and
   shows that element's label. */
#cursor-dot.is-pill {
  width: 122px;
  height: 40px;
  margin: -20px 0 0 -61px;
  background: rgba(240, 240, 245, 0.6);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: inset 1px 1px 5px rgba(255, 255, 255, 0.8);
}
/* The pill's frost is a light-on-light effect: the same white inner highlight
   over a dark page reads as a glowing slab. Dark gets the same idea inverted —
   a raised panel with a faint light edge — rather than the same values. */
:root[data-theme="dark"] #cursor-dot.is-pill {
  background: rgba(40, 40, 44, 0.72);
  box-shadow: inset 1px 1px 5px rgba(255, 255, 255, 0.16);
}

#cursor-label {
  font-family: "DM Sans", sans-serif;
  font-size: 13px;
  letter-spacing: 0.39px;
  color: var(--ink);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.15s ease;
}
#cursor-dot.is-pill #cursor-label { opacity: 1; }

/* JS adds this only on a fine pointer, so touch keeps its native behaviour.
   .dot-live waits for the first mousemove so the dot never flashes at 0,0. */
/* Everything, not a list of the usual suspects: the native cursor must never
   show beside the dot. A per-element list kept missing things (the opened
   sketchbook card set its own `cursor: default`), and !important also beats
   any page stylesheet or inline style a script writes. The one deliberate
   exception is the playground mat's grab hand, re-allowed in sketchbook.css
   with higher specificity - the dot stands down over it (data-cursor-none).
   Cross-origin iframes (YouTube) still draw their own cursor; site.js hides
   the dot over them so there is still only one. */
body.has-dot,
body.has-dot *,
body.has-dot *::before,
body.has-dot *::after { cursor: none !important; }
body.has-dot.dot-live #cursor-dot { opacity: 1; }
/* ...except over something that draws its own cursor. Written at the same
   weight as the rule above it, or the dot it is trying to hide wins. */
body.has-dot.dot-live #cursor-dot.is-hidden { opacity: 0; }

/* ---------- layout + type scale ---------- */

main { padding-bottom: 120px; }

/* The vertical rhythm between top-level groups. Named because it is not only
   the gap between sections: a section that stacks its own groups (about me's
   Experience over Education) has to open the same distance, or the second
   group reads as belonging to whatever follows it. */
:root { --block-gap: 120px; }

.block {
  width: min(var(--col), calc(100% - 80px));
  margin: 0 auto var(--block-gap);
}
.block:last-child { margin-bottom: 0; }

/* Uppercase regardless of how the label is written in the markup. The Framer
   original lands on all-caps by accident — it splits each label into
   per-character spans and sets text-transform: capitalize, so every character
   becomes its own word start. This states the intent directly. */
.eyebrow {
  font-family: "Play", sans-serif;
  font-size: 16px;
  color: var(--accent);
  margin: 0 0 20px;
  text-transform: uppercase;
}

h1 {
  font-family: "DM Sans", sans-serif;
  font-size: 42px;
  font-weight: 500;
  color: var(--head);
  margin: 0 0 18px;
}

.lede {
  font-family: "DM Sans", sans-serif;
  font-size: 30px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink);
  margin: 0;
  max-width: 820px;
}

.statement {
  font-size: 22px;
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 24px;
}

.body-copy {
  font-size: 18px;
  line-height: 1.65;
  color: var(--body);
  margin: 0 0 20px;
}

.accent { color: var(--accent); }
a.accent { text-decoration: none; }
a.accent:hover { text-decoration: underline; }

figcaption,
.caption {
  font-size: 16px;
  font-weight: 300;
  color: var(--caption);
  margin-top: 12px;
}

/* ---------- responsive ---------- */

@media (max-width: 900px) {
  /* the tagline group is decorative; drop it before it can crowd the crumb */
  .topbar-right { display: none; }
  .topbar-left { white-space: nowrap; }
  :root { --block-gap: 88px; }
  .lede { font-size: 24px; }
  h1 { font-size: 34px; }
  .statement { font-size: 20px; }
}

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