/* ==========================================================================
   Shaan Mehta — personal site
   1. Tokens
   2. Reset & base
   3. Particle canvas
   4. Layout
   5. Load reveal
   6. Header (name, tagline, bio, flipping word)
   7. Sections: experience / projects
   8. Footer: socials + signature
   9. Theme toggle
  10. Email dialog
  11. Robot (opt-in pointer toy)
  12. Utilities & motion preferences
   ========================================================================== */

/* ==========================================================================
   1. TOKENS
   :root is the DARK theme (the site default). [data-theme="light"] overrides.
   No colour is hardcoded anywhere outside this block.
   ========================================================================== */

:root {
  /* -- surfaces & text -- */
  --bg:              #0A0A0A;
  --text:            #F2F0E9;
  --text-strong:     #FFFFFF;
  --text-muted:      #858179;
  --rule:            rgba(242, 240, 233, 0.12);
  --backdrop:        rgba(10, 10, 10, 0.72);

  /* Outbound links inside prose — the degree and every company name share this
     one shade. Deeper and warmer than the body text so a link reads as a link
     rather than blending into the near-white copy. */
  --company:         #C79A73;

  /* "winter 2027 internships" only. Nothing else uses this: it is a saturated
     amber-gold, 30.9 dE from its nearest neighbour in the palette, so it can
     never be mistaken for a link or for a rotating accent word. */
  --seeking:         #FFB240;

  /* -- particle field: base RGB only; alpha comes from the draw code so the
        field can match the reference's exact opacity maths -- */
  --particle-rgb:    255, 255, 255;

  /* -- accent: burnt-terracotta family, hue sweeps ~9deg -> ~34deg -- */
  --accent:          #EC9A7B;
  --accent-1:        #E99284;  /* lifting      */
  --accent-2:        #EC9A7B;  /* swimming     */
  --accent-3:        #EDA475;  /* cooking      */
  --accent-4:        #E9AE79;  /* cinephiling  */
  --accent-5:        #E2B47E;  /* sidequesting */

  /* -- type -- */
  --font-serif: "IBM Plex Serif", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --font-sans:  Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Every size below is fluid between a 320px phone and a 2560px display, so
     the page reads at the right scale on a phone, a laptop and a TV. */
  --size-name:  clamp(2.25rem, 1.82143rem + 2.14286vw, 5.25rem);    /* 36   -> 84   */
  --size-title: clamp(1.25rem, 1.125rem + 0.625vw, 2.125rem);       /* 20   -> 34   */
  --size-body:  clamp(0.96875rem, 0.901786rem + 0.334821vw, 1.4375rem);  /* 15.5 -> 23 */
  --size-small: clamp(0.875rem, 0.816964rem + 0.290179vw, 1.28125rem);   /* 14 -> 20.5 */

  /* icons scale with the type rather than sitting at a fixed pixel size */
  --icon-size:    clamp(1.625rem, 1.535714rem + 0.446429vw, 2.25rem);  /* 26 -> 36 */
  --icon-size-sm: clamp(1.25rem, 1.178571rem + 0.357143vw, 1.75rem);   /* 20 -> 28 */
  --icon-size-xs: clamp(1rem, 0.933036rem + 0.334821vw, 1.4375rem);    /* 16 -> 23 */

  /* -- rhythm -- */
  /* Wide enough that an experience row (logo + role + dates) still fits on one
     line at the larger type sizes above. */
  --measure:     clamp(45rem, 36.875rem + 15.625vw, 62rem);   /* 720px -> 992px */
  --gap-section: clamp(3rem, 1.4rem + 8vw, 6rem);
  --gap-block:   clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
  --gap-intro:   clamp(1.4rem, 1rem + 2vw, 2.5rem);   /* name -> tagline -> bio */
  --page-pad:    clamp(1.25rem, 0.85rem + 2vw, 3rem);

  /* -- experience logo box: caps BOTH axes so a wide wordmark and a stacked
        mark still read at the same optical weight and the rows line up -- */
  --logo-w: clamp(4rem, 3.5rem + 1.6vw, 6.875rem);
  --logo-h: clamp(1.6875rem, 1.5rem + 0.7vw, 2.875rem);

  /* -- flip timing -- */
  --flip-dur:  600ms;
  --flip-ease: cubic-bezier(0.65, 0, 0.35, 1);

  /* -- load reveal --------------------------------------------------------
     THESE TWO LINES CONTROL THE WHOLE INTRO ANIMATION:
       --reveal-dur   how long each block takes to ease in
       --reveal-step  the gap between one block starting and the next
     Total run time = --reveal-dur + (--reveal-step x 4).
     Raise either to slow it down, lower to speed it up. Nothing else to edit —
     the five delays below are derived from --reveal-step, and script.js reads
     the computed values back so its cleanup timing follows automatically. */
  --reveal-dur:  760ms;
  --reveal-step: 150ms;
  --reveal-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

[data-theme="light"] {
  --bg:              #FAF7EC;
  --text:            #14110E;
  --text-strong:     #0B0907;
  --text-muted:      #736E60;
  --rule:            rgba(20, 17, 14, 0.14);
  --backdrop:        rgba(250, 247, 236, 0.72);

  /* darker than the old #7A4A32 so links carry more weight on cream */
  --company:         #5E3722;
  /* deep burnt rust — 16.9 dE clear of every accent, and nothing else uses it */
  --seeking:         #A32B00;

  --particle-rgb:    122, 115, 101;

  --accent:          #9B4022;
  --accent-1:        #94382A;
  --accent-2:        #9B4022;
  --accent-3:        #A0491D;
  --accent-4:        #9A5019;
  --accent-5:        #8F5518;
}

/* ==========================================================================
   2. RESET & BASE
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--size-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  transition: background-color 320ms ease, color 320ms ease;
}

h1, h2, h3, p, ul, figure { margin: 0; }
ul { list-style: none; padding: 0; }
img { display: block; max-width: 100%; }

a { color: inherit; text-decoration: none; }

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

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

/* Artwork that only shows in one theme (company logos, the SM signature). */
.swap-light { display: none; }
[data-theme="light"] .swap-dark  { display: none; }
[data-theme="light"] .swap-light { display: block; }

/* The pinned controls (theme toggle, robot switch) sit above the page, so
   without this the content would scroll visibly through them. This soft scrim
   fades the page out just before it reaches them — it sits above the content
   but below the controls and the robot. */
body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: clamp(4.75rem, 3.75rem + 3vw, 7.5rem);
  z-index: 9;
  pointer-events: none;
  background: linear-gradient(to bottom, var(--bg) 0%, var(--bg) 34%, transparent 100%);
}

/* ==========================================================================
   3. PARTICLE CANVAS
   ========================================================================== */

.field {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ==========================================================================
   4. LAYOUT
   ========================================================================== */

.page {
  position: relative;
  z-index: 1;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--page-pad);
  padding-block: clamp(5.5rem, 4rem + 7vw, 9rem) clamp(3.5rem, 2.5rem + 4vw, 6rem);
}

.main { margin-top: var(--gap-section); }

.section + .section { margin-top: var(--gap-section); }

.section__title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--size-title);
  line-height: 1.25;
  color: var(--text-strong);
  margin-bottom: var(--gap-block);
}

/* ==========================================================================
   5. LOAD REVEAL
   The canvas and the theme toggle carry no [data-reveal], so both are already
   painted on the very first frame. Everything else eases in on a short stagger:
   name, then tagline + bio together, then experience, projects, footer.
   `both` fill mode holds the start state through the delay, so nothing flashes.
   script.js strips the attributes once the sequence finishes.
   ========================================================================== */

@keyframes reveal {
  from { opacity: 0; transform: translateY(14px); filter: blur(7px); }
  to   { opacity: 1; transform: none;             filter: none; }
}

[data-reveal] {
  animation: reveal var(--reveal-dur) var(--reveal-ease) both;
}

/* all five derive from --reveal-step, so the cadence is tuned in one place */
[data-reveal="1"] { animation-delay: 0ms; }
[data-reveal="2"] { animation-delay: calc(var(--reveal-step) * 1); }
[data-reveal="3"] { animation-delay: calc(var(--reveal-step) * 2); }
[data-reveal="4"] { animation-delay: calc(var(--reveal-step) * 3); }
[data-reveal="5"] { animation-delay: calc(var(--reveal-step) * 4); }

/* ==========================================================================
   6. HEADER
   ========================================================================== */

.name {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--size-name);
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--text-strong);
}

.tagline {
  margin-top: var(--gap-intro);
  color: var(--text);
}

/* Prose links — the degree and each company share one shade. */
.link {
  color: var(--company);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 200ms ease, color 200ms ease;
}

.link:hover,
.link:focus-visible { text-decoration-color: currentColor; }

/* -- bio ------------------------------------------------------------------ */

.bio {
  margin-top: var(--gap-intro);
  display: grid;
  gap: 0.35rem;
  color: var(--text);
}

.bio__item {
  position: relative;
  padding-left: 1.2em;
}

/* small rotated square — a quiet geometric marker, no list glyphs */
.bio__item::before {
  content: "";
  position: absolute;
  left: 1px;
  top: calc(0.8em - 0.19em);
  width: 0.38em;
  height: 0.38em;
  background-color: var(--accent);
  transform: rotate(45deg);
  opacity: 0.8;
}

.bio__seeking { color: var(--seeking); }

/* -- the flipping word ---------------------------------------------------- */

.flip {
  display: inline-block;
  position: relative;
  perspective: 360px;
  vertical-align: baseline;
}

/* Every word stacked in one grid cell: the box is permanently as wide as the
   widest word, so the sentence never reflows mid-flip. */
.flip__sizer {
  display: grid;
  visibility: hidden;
}

.flip__sizer i {
  grid-area: 1 / 1;
  font-style: normal;
  white-space: nowrap;
}

.flip__drum {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform var(--flip-dur) var(--flip-ease);
}

.flip__face {
  position: absolute;
  inset: 0;
  white-space: nowrap;
  backface-visibility: hidden;
  font-weight: 500;
  /* --flip-half is set by script.js to half the face height, so the two faces
     sit on adjacent sides of a cube of exactly line height. */
  transition: filter var(--flip-dur) var(--flip-ease);
}

.flip__face--front { transform: rotateX(0deg)  translateZ(var(--flip-half, 0.6em)); }
.flip__face--back  { transform: rotateX(90deg) translateZ(var(--flip-half, 0.6em)); }

/* Subtle depth shading during the flip: whichever face is turning away dims,
   which sells the rotation without drawing a visible cube box. */
.flip__face--back { filter: brightness(0.72); }
.flip.is-flipping .flip__face--front { filter: brightness(0.72); }
.flip.is-flipping .flip__face--back  { filter: brightness(1); }

.flip.is-flipping .flip__drum { transform: rotateX(-90deg); }

/* Applied for one frame while the drum snaps back to 0deg and the incoming
   word is moved onto the front face — the reset must never animate. */
.flip.is-resetting .flip__drum,
.flip.is-resetting .flip__face { transition: none; }

.flip__face[data-word="0"] { color: var(--accent-1); }
.flip__face[data-word="1"] { color: var(--accent-2); }
.flip__face[data-word="2"] { color: var(--accent-3); }
.flip__face[data-word="3"] { color: var(--accent-4); }
.flip__face[data-word="4"] { color: var(--accent-5); }

/* ==========================================================================
   7. SECTIONS
   ========================================================================== */

.rows { display: grid; gap: clamp(1.15rem, 0.9rem + 1.2vw, 2rem); }

/* -- experience ----------------------------------------------------------- */

/* The row itself is NOT a link. Only "@ Company" is clickable, so hovering the
   role text or the dates does nothing. */
.row {
  display: grid;
  grid-template-columns: var(--logo-w) minmax(0, 1fr) auto;
  grid-template-areas: "logo role date";
  align-items: center;
  column-gap: clamp(0.9rem, 0.6rem + 1.2vw, 1.75rem);
  row-gap: 0.15rem;
}

.row__logo {
  grid-area: logo;
  display: block;
  width: var(--logo-w);
  height: var(--logo-h);
}

.logo {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left center;
}

.row__role {
  grid-area: role;
  color: var(--text);
}

.row__company {
  /* if the role has to wrap, break before the "@" rather than inside the
     company name */
  white-space: nowrap;
  color: var(--company);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 200ms ease;
}

.row__company:hover,
.row__company:focus-visible { text-decoration-color: currentColor; }

.row__date {
  grid-area: date;
  color: var(--text-muted);
  font-size: var(--size-small);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* -- projects ------------------------------------------------------------- */

.project__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* pull back the padding that gives the icon links their touch target */
  margin-block: -0.55rem;
}

.project__name {
  font-family: var(--font-sans);
  font-size: var(--size-body);
  font-weight: 500;
  line-height: 1.4;
  color: var(--text-strong);
}

.project__links {
  display: flex;
  align-items: center;
  margin-right: -0.7rem;
}

.iconlink {
  display: grid;
  place-items: center;
  width: max(44px, calc(var(--icon-size-sm) + 1.25rem));
  height: max(44px, calc(var(--icon-size-sm) + 1.25rem));
  color: var(--text-muted);
  transition: color 200ms ease;
}

.iconlink:hover,
.iconlink:focus-visible { color: var(--accent); }

.project__desc {
  margin-top: 0.2rem;
  color: var(--text-muted);
  font-size: var(--size-small);
  line-height: 1.55;
  max-width: 56ch;
}

.icon { width: var(--icon-size); height: var(--icon-size); }
.icon--sm { width: var(--icon-size-xs); height: var(--icon-size-xs); }
.project__links .icon { width: var(--icon-size-sm); height: var(--icon-size-sm); }

/* ==========================================================================
   8. FOOTER
   Socials on the left, the signature block on the right, both sitting under
   the same hairline. The label expands in flow from zero width, easing the
   neighbouring icons along with it — the same mechanic bry4n.co uses.
   ========================================================================== */

.footer { margin-top: var(--gap-section); }

.footer__rule {
  height: 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--rule);
}

.footer__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  flex-wrap: wrap;
  margin-top: 1.15rem;
}

.socials {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: -0.625rem;   /* offset the icon padding so it aligns to the column */
}

.social {
  display: inline-flex;
  align-items: center;
  padding: 0.625rem;        /* icon + 20px = comfortably past a 44px target */
  color: var(--text-muted);
  transition: color 200ms ease;
}

.social:hover,
.social:focus-visible { color: var(--text-strong); }

.social .icon {
  flex-shrink: 0;
  transition: transform 500ms ease-out;
}

.social:hover .icon,
.social:focus-visible .icon { transform: scale(1.1); }

.social__label {
  display: inline-block;
  max-width: 0;
  margin-left: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: var(--size-small);
  line-height: 1.2;
  opacity: 0;
  transition: 500ms ease-out;
}

.social:hover .social__label,
.social:focus-visible .social__label {
  max-width: 200px;
  margin-left: 0.5rem;
  opacity: 1;
}

/* -- signature ------------------------------------------------------------ */

.footer__sign {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  margin-left: auto;
  color: var(--text-muted);
  font-size: var(--size-small);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.footer__mark {
  width: calc(var(--icon-size) * 1.15);
  height: calc(var(--icon-size) * 1.15);
  flex-shrink: 0;
  opacity: 0.9;
}

/* ==========================================================================
   9. THEME TOGGLE
   ========================================================================== */

.theme-toggle {
  position: fixed;
  top: clamp(0.85rem, 0.4rem + 1.4vw, 1.75rem);
  left: clamp(0.85rem, 0.4rem + 1.4vw, 1.75rem);
  z-index: 10;
  display: grid;
  place-items: center;
  width: max(44px, calc(var(--icon-size) + 1.25rem));
  height: max(44px, calc(var(--icon-size) + 1.25rem));
  color: var(--text-muted);
  transition: color 200ms ease;
}

.theme-toggle:hover,
.theme-toggle:focus-visible { color: var(--text-strong); }

/* both icons live in the same grid cell — swapping never shifts layout */
.theme-toggle__icons { display: grid; }

.theme-toggle__icons .icon {
  grid-area: 1 / 1;
  transition: opacity 300ms ease, transform 420ms cubic-bezier(0.65, 0, 0.35, 1);
}

.icon--sun  { opacity: 1; transform: rotate(0deg)   scale(1); }
.icon--moon { opacity: 0; transform: rotate(-70deg) scale(0.7); }

[data-theme="light"] .icon--sun  { opacity: 0; transform: rotate(70deg) scale(0.7); }
[data-theme="light"] .icon--moon { opacity: 1; transform: rotate(0deg)  scale(1); }

/* ==========================================================================
   10. EMAIL DIALOG
   ========================================================================== */

.modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: var(--page-pad);
}

.modal[hidden] { display: none; }

.modal__backdrop {
  position: absolute;
  inset: 0;
  background-color: var(--backdrop);
}

.modal__panel {
  position: relative;
  width: min(100%, 30rem);
  padding: 1.5rem 1.5rem 0.875rem;
  background-color: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 4px;
}

.modal__text {
  color: var(--text);
  font-size: var(--size-small);
  line-height: 1.6;
  padding-right: 2rem;
}

.modal__close {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  display: grid;
  place-items: center;
  width: max(44px, calc(var(--icon-size) + 1rem));
  height: max(44px, calc(var(--icon-size) + 1rem));
  color: var(--text-muted);
  transition: color 200ms ease;
}

.modal__close:hover,
.modal__close:focus-visible { color: var(--text-strong); }

/* -- the two addresses ---------------------------------------------------- */

.emails { margin-top: 1.1rem; }

.email {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1.25rem;
  flex-wrap: wrap;
  padding-block: 0.6rem;
  border-top: 1px solid var(--rule);
}

/* The 44px copy button is taller than its text, so the last row would leave
   more air below it than the panel has above the heading. Drop its padding and
   the block sits optically centred. */
.email:last-child { padding-bottom: 0; }

/* flex-basis is the wrap trigger: once the panel is too narrow for the address
   and the button side by side, the button drops to its own line instead of
   squeezing the address. */
.email__line {
  flex: 1 1 13rem;
  color: var(--text);
  font-size: var(--size-small);
  line-height: 1.5;
}

.email__label {
  color: var(--text-muted);
  font-weight: 500;
}

.email__copy {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
  min-height: 44px;
  padding-inline: 0.55rem;
  /* negative on both sides so the button reads flush with the panel edge when
     it sits beside the address, and flush with the text when it wraps under */
  margin-inline: -0.55rem;
  color: var(--text-muted);
  font-size: 0.875rem;
  border-radius: 4px;
  transition: color 200ms ease;
}

.email__copy:hover,
.email__copy:focus-visible { color: var(--accent); }

.email__copy.is-copied { color: var(--accent); }

/* ==========================================================================
   11. ROBOT
   An opt-in pointer toy. The control only exists on a fine, hovering pointer
   at tablet width or wider, so phones never get it. Everything here is inert
   until .robot-on lands on <html>.
   ========================================================================== */

/* -- the switch ----------------------------------------------------------- */

.robot-ui { display: none; }

@media (hover: hover) and (pointer: fine) and (min-width: 48em) {
  .robot-ui {
    position: fixed;
    top: clamp(0.85rem, 0.4rem + 1.4vw, 1.75rem);
    right: clamp(0.85rem, 0.4rem + 1.4vw, 1.75rem);
    z-index: 16;
    display: flex;
    align-items: center;
    gap: 0.7rem;
  }
}

.robot-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  min-height: 44px;
  color: var(--text-muted);
  font-size: var(--size-small);
  transition: color 200ms ease;
}

.robot-switch:hover,
.robot-switch:focus-visible { color: var(--text-strong); }

/* Only one of these is ever in the layout. The row is anchored to the right,
   so swapping them grows the text leftward and the switch never shifts. */
.robot-switch__msg { color: inherit; }
.robot-switch__msg--on { display: none; }

.robot-switch[aria-checked="true"] .robot-switch__msg--off { display: none; }

.robot-switch[aria-checked="true"] .robot-switch__msg--on {
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  color: var(--text);
}

.robot-switch__key {
  font-family: inherit;
  font-size: 0.9em;
  font-weight: 500;
  line-height: 1;
  padding: 0.25em 0.45em;
  color: var(--text-strong);
  background-color: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 4px;
}

.robot-switch__track {
  position: relative;
  width: 2.5rem;
  height: 1.375rem;
  flex-shrink: 0;
  border: 1px solid var(--rule);
  border-radius: 999px;
  transition: background-color 220ms ease, border-color 220ms ease;
}

.robot-switch__thumb {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  background-color: var(--text-muted);
  transform: translateY(-50%);
  transition: transform 220ms cubic-bezier(0.65, 0, 0.35, 1), background-color 220ms ease;
}

.robot-switch[aria-checked="true"] .robot-switch__track {
  background-color: var(--accent);
  border-color: var(--accent);
}

.robot-switch[aria-checked="true"] .robot-switch__thumb {
  background-color: var(--bg);
  transform: translateY(-50%) translateX(1.25rem);
}

/* -- the robot ------------------------------------------------------------ */

.robot {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 12;
  display: none;
  width: 54px;
  height: 76px;
  pointer-events: none;
  /* tracks the theme automatically: near-black on cream, off-white on black */
  --robot-body:   var(--text);
  --robot-face:   var(--bg);
  --robot-accent: var(--accent);
}

.robot.is-live { display: block; }

.robot svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.robot.is-live svg {
  animation: robot-in 300ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes robot-in {
  from { opacity: 0; transform: scale(0.35); }
  to   { opacity: 1; transform: none; }
}

.robot__fill   { fill: var(--robot-body); }
.robot__accent { fill: var(--robot-accent); }
.robot__face   { fill: var(--robot-face); }
.robot__stroke { stroke: var(--robot-body); stroke-width: 2; stroke-linecap: round; }
.robot__limb   { stroke: var(--robot-body); stroke-width: 5; stroke-linecap: round; }
.robot__smile  { stroke: var(--robot-face); stroke-width: 1.8; stroke-linecap: round; }

/* joints — rotate about the shoulder / hip, in viewBox units */
.robot__arm,
.robot__leg {
  transform-box: view-box;
  transition: transform 130ms cubic-bezier(0.33, 1, 0.68, 1);
}

.robot__arm--l { transform-origin: 13.5px 36px; }
.robot__arm--r { transform-origin: 34.5px 36px; }
.robot__leg--l { transform-origin: 19.5px 50px; }
.robot__leg--r { transform-origin: 28.5px 50px; }

.robot__rig {
  transform-box: view-box;
  transform-origin: 24px 52px;
  transition: transform 160ms ease-out;
}

.robot__bob {
  transform-box: view-box;
  transform-origin: 24px 34px;
}

/* -- running: cadence comes from --run-dur, which JS drives off pointer speed */

.robot--run .robot__arm--l { animation: rb-arm var(--run-dur, 420ms) ease-in-out infinite; }
.robot--run .robot__arm--r { animation: rb-arm var(--run-dur, 420ms) ease-in-out infinite reverse; }
.robot--run .robot__leg--l { animation: rb-leg var(--run-dur, 420ms) ease-in-out infinite reverse; }
.robot--run .robot__leg--r { animation: rb-leg var(--run-dur, 420ms) ease-in-out infinite; }
.robot--run .robot__bob    { animation: rb-bob calc(var(--run-dur, 420ms) / 2) ease-in-out infinite; }

@keyframes rb-arm { 0%, 100% { transform: rotate(36deg); }  50% { transform: rotate(-36deg); } }
@keyframes rb-leg { 0%, 100% { transform: rotate(-32deg); } 50% { transform: rotate(32deg); } }
@keyframes rb-bob { 0%, 100% { transform: translateY(0); }  50% { transform: translateY(-1.8px); } }

.robot--idle .robot__bob { animation: rb-breathe 2.8s ease-in-out infinite; }
@keyframes rb-breathe { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.1px); } }

/* -- bracing poses: arms (and a braced stance) shove toward the text -------- */

.robot--push-left  .robot__arm--l { transform: rotate(86deg); }
.robot--push-left  .robot__arm--r { transform: rotate(104deg); }
.robot--push-left  .robot__leg--l { transform: rotate(30deg); }
.robot--push-left  .robot__leg--r { transform: rotate(-16deg); }
.robot--push-left  .robot__rig    { transform: rotate(-7deg); }

.robot--push-right .robot__arm--l { transform: rotate(-104deg); }
.robot--push-right .robot__arm--r { transform: rotate(-86deg); }
.robot--push-right .robot__leg--l { transform: rotate(16deg); }
.robot--push-right .robot__leg--r { transform: rotate(-30deg); }
.robot--push-right .robot__rig    { transform: rotate(7deg); }

/* splayed out, not straight up — otherwise the arms vanish behind the head */
.robot--push-up    .robot__arm--l { transform: rotate(149deg); }
.robot--push-up    .robot__arm--r { transform: rotate(-149deg); }
.robot--push-up    .robot__leg--l { transform: rotate(15deg); }
.robot--push-up    .robot__leg--r { transform: rotate(-15deg); }
.robot--push-up    .robot__rig    { transform: translateY(2px); }

.robot--push-down  .robot__arm--l { transform: rotate(20deg); }
.robot--push-down  .robot__arm--r { transform: rotate(-20deg); }
.robot--push-down  .robot__leg--l { transform: rotate(36deg); }
.robot--push-down  .robot__leg--r { transform: rotate(-36deg); }
.robot--push-down  .robot__rig    { transform: translateY(-2px); }

/* -- the text getting shoved out of the way -------------------------------- */

/* Words are pre-wrapped but stay plain inline (zero layout impact) until the
   toy is switched on — a transform needs a block-ish box to take effect. The
   .ru units are already inline-block/flex/grid, so they are left alone: forcing
   a display on them would break the theme-swapped marks and the flex signature. */
.robot-on .rw { display: inline-block; }

/* ==========================================================================
   12. UTILITIES & MOTION PREFERENCES
   ========================================================================== */

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

/* -- narrow screens ------------------------------------------------------- */

@media (max-width: 34.375em) {           /* 550px */
  .row {
    grid-template-columns: var(--logo-w) minmax(0, 1fr);
    grid-template-areas:
      "logo role"
      "logo date";
    align-items: center;
  }

  .row__date { justify-self: start; }

  /* stack the signature under the socials instead of crushing the row */
  .footer__sign { margin-left: 0; }
}

/* -- reduced motion ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms;
    animation-iteration-count: 1;
    transition-duration: 0.001ms;
  }

  /* show everything immediately — no stagger, no blur, no travel */
  [data-reveal] { animation: none; }

  .flip__drum { transform: none; }
  .flip.is-flipping .flip__drum { transform: none; }

  .flip__face {
    transform: none;
    backface-visibility: visible;
    filter: none;
    transition: opacity 300ms ease;
  }

  .flip__face--back { opacity: 0; filter: none; }
  .flip.is-flipping .flip__face--front { opacity: 0; filter: none; }
  .flip.is-flipping .flip__face--back  { opacity: 1; }

  /* the label reveal stays, just without the easing */
  .social__label { transition-duration: 0.001ms; }
  .social .icon { transition-duration: 0.001ms; }
}
