/* ==========================================================================
   Base + page layout
   ========================================================================== */

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

:root {
  /* One knob for vertical rhythm between sections. */
  --section-pad: clamp(40px, 5vw, 76px);
  /* Where a scrolled-to heading should land: clear of the 64px sticky bar. */
  --scroll-offset: 88px;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--scroll-offset);

  /* `clip`, not `hidden`. Three reasons, all of which bit this page:
     - `hidden` turns the viewport into a scroll container, which breaks
       `position: sticky` on the top app bar.
     - `overflow-x: hidden` next to a visible y-axis silently promotes
       overflow-y to `auto`, adding a second scroller.
     - `hidden` on <body> alone never reliably contained the overflow on
       Android in the first place.
     `clip` creates no scroll container, so sticky and vertical scrolling are
     both untouched. See .portrait__orbit for what actually overflows. */
  overflow-x: clip;

  /* Chrome on Android inflates text ("font boosting") whenever the layout
     viewport is wider than the visual one — which is exactly what the orbit's
     overflow was causing, and why the bottom nav appeared to zoom until its
     last item fell off the screen. Pin the multiplier at 100%. This does not
     disable pinch-zoom; it only stops the automatic inflation. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--background);
  color: var(--on-background);
  font-family: var(--font-plain);
  font-size: var(--type-body-lg);
  line-height: 1.55;
  font-variation-settings: "wdth" 100, "GRAD" 0, "ROND" 0;
  font-optical-sizing: auto;   /* let opsz 6–144 respond to font-size */
  -webkit-font-smoothing: antialiased;
  transition: background var(--dur-long-2) var(--ease-standard),
              color      var(--dur-long-2) var(--ease-standard);
}

h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font: inherit; }
ul { margin: 0; padding: 0; }

::selection { background: var(--primary-container); color: var(--on-primary-container); }

/* Scrollbar, themed */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--surface-container-low); }
::-webkit-scrollbar-thumb {
  background: var(--outline-variant);
  border-radius: var(--shape-full);
  border: 3px solid var(--surface-container-low);
}
::-webkit-scrollbar-thumb:hover { background: var(--outline); }

/* --------------------------------------------------------------------------
   Type helpers
   -------------------------------------------------------------------------- */
/* Display sizes carry a touch of ROND — Google Sans Flex's roundness axis is
   what gives M3 Expressive headlines their softer, friendlier terminals.
   font-variation-settings replaces the whole list, so wdth/GRAD are repeated. */
.display-lg  { font-size: var(--type-display-lg);  font-weight: 800; letter-spacing: -0.035em; line-height: 0.95;
               font-variation-settings: "wdth" 100, "GRAD" 0, "ROND" 45; }
.display-md  { font-size: var(--type-display-md);  font-weight: 800; letter-spacing: -0.028em;
               font-variation-settings: "wdth" 100, "GRAD" 0, "ROND" 45; }
.headline-lg { font-size: var(--type-headline-lg); font-weight: 700;
               font-variation-settings: "wdth" 100, "GRAD" 0, "ROND" 30; }
.headline-md { font-size: var(--type-headline-md); font-weight: 700;
               font-variation-settings: "wdth" 100, "GRAD" 0, "ROND" 30; }
.title-lg    { font-size: var(--type-title-lg);    font-weight: 700; }
.title-md    { font-size: var(--type-title-md);    font-weight: 600; }
.body-lg     { font-size: var(--type-body-lg);     font-weight: 400; }
.body-md     { font-size: var(--type-body-md);     font-weight: 400; }
.label-lg    { font-size: var(--type-label-lg);    font-weight: 600; letter-spacing: 0.1px; }
.on-variant  { color: var(--on-surface-variant); }
.text-primary{ color: var(--primary); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px 6px 10px;
  border-radius: var(--shape-full);
  background: var(--tertiary-container);
  color: var(--on-tertiary-container);
  font-size: var(--type-label-md);
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
}

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

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */
.shell { width: min(1180px, 100% - clamp(32px, 8vw, 96px)); margin-inline: auto; }

/* A section's own top padding sits between the anchor point and the heading,
   so cancel it with a negative scroll-margin. Net effect: the heading lands
   exactly --scroll-offset from the top, regardless of how big the padding is. */
section[id] {
  padding-block: var(--section-pad);
  scroll-margin-top: calc(-1 * var(--section-pad));
}
#home { scroll-margin-top: 0; }

.section-head {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 62ch;
  margin-bottom: clamp(32px, 5vw, 56px);
}

/* --------------------------------------------------------------------------
   Ambient background — soft coral/olive glows behind everything
   -------------------------------------------------------------------------- */
.ambient {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.ambient__blob {
  position: absolute;
  filter: blur(90px);
  opacity: 0.5;
  animation: drift 26s ease-in-out infinite alternate;
}
.ambient__blob--1 {
  width: 46vmax; height: 46vmax;
  top: -16vmax; right: -12vmax;
  background: var(--primary);
  border-radius: 46% 54% 38% 62% / 58% 40% 60% 42%;
}
.ambient__blob--2 {
  width: 38vmax; height: 38vmax;
  bottom: -14vmax; left: -12vmax;
  background: var(--tertiary);
  border-radius: 62% 38% 55% 45% / 40% 60% 40% 60%;
  animation-delay: -9s;
}
.ambient__blob--3 {
  width: 28vmax; height: 28vmax;
  top: 45%; left: 55%;
  background: var(--secondary);
  border-radius: 40% 60% 65% 35% / 55% 45% 55% 45%;
  animation-delay: -17s;
  opacity: 0.32;
}
[data-theme="dark"] .ambient__blob { opacity: 0.28; }
[data-theme="dark"] .ambient__blob--3 { opacity: 0.18; }
@keyframes drift {
  from { transform: translate3d(0,0,0) rotate(0deg)   scale(1); }
  to   { transform: translate3d(4vw,3vh,0) rotate(40deg) scale(1.14); }
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: center;
  gap: clamp(32px, 6vw, 72px);
  padding-block: clamp(48px, 7vw, 96px) clamp(64px, 9vw, 120px);
}
.hero__copy { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; }
.hero__title .line { display: block; }
.hero__title .line--intro {
  font-size: 0.3em;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--on-surface-variant);
  margin-bottom: 0.28em;
}
.hero__title .accent {
  background: linear-gradient(100deg, var(--primary), var(--tertiary));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero__lede { max-width: 52ch; color: var(--on-surface-variant); font-size: clamp(1rem, 1.6vw, 1.25rem); }
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* Morphing portrait — path swapped by js/main.js */
.hero__portrait {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  width: 100%;
  max-width: 420px;
  margin-inline: auto;
}
.portrait__shape {
  width: 100%;
  height: 100%;
  background: linear-gradient(145deg, var(--primary), var(--tertiary));
  clip-path: url(#morph-clip);
  display: grid;
  place-items: center;
}
/* Everything inside the shape shares one grid cell so it stacks rather than
   flows — a photo dropped in later lands on top of the medallion, not under it. */
.portrait__shape > * { grid-area: 1 / 1; }

/* --on-primary is the correct role in both themes: the gradient is built from
   --primary and --tertiary, which are mid-tones on light and light tints on
   dark, so the ink flips with them and stays legible either way. */
.portrait__bloom {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--on-primary);
  stroke-linejoin: round;
}

/* transform-box: view-box is what makes `center` resolve to the viewBox centre
   (50,50). Without it transform-origin falls back to the SVG origin and every
   ring swings around the top-left corner — the same trap as .boot__dot. */
.portrait__ring {
  transform-box: view-box;
  transform-origin: center;
  animation-name: spin;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.portrait__ring--1 { stroke-width: 0.8; opacity: 0.32; animation-duration: 64s; }
.portrait__ring--2 {
  stroke-width: 1.1;
  opacity: 0.48;
  animation-duration: 44s;
  animation-direction: reverse;   /* counter-rotation stops it reading as one disc */
}
.portrait__ring--3 { stroke-width: 1.5; opacity: 0.66; animation-duration: 30s; }

/* Static core — a filled 6-lobe ring, the same figure as the favicon and the
   header mark. Deliberately doesn't turn: it anchors the centre while
   everything around it moves. */
.portrait__core { fill: var(--on-primary); stroke: none; opacity: 0.9; }

/* Was a square div with border-radius:50% and `animation: spin`. Rotating a
   square makes its layout box the rotated bounding box — √2× the side at 45°,
   ~475px against a 346px shell on a phone. The visible ring never moved, but
   that phantom box was real overflow: it let the whole page drag sideways on
   mobile and triggered Chrome's Android text inflation.
   It could not be inset away — a ring small enough to keep its rotated box
   inside the shell would be smaller than the portrait it rings. So the ring no
   longer rotates at all: the dashes travel instead, via stroke-dashoffset. No
   transform, so the layout box is exactly the inset box. Don't reintroduce a
   rotation here.
   pathLength=360 normalises the dash maths away from the radius: the pattern
   below is a 4-unit cycle, so 90 dashes fit exactly and a -360 offset is one
   seamless loop with no visible seam. */
.portrait__orbit {
  position: absolute;
  inset: -6%;
  fill: none;
  stroke: var(--outline-variant);
  stroke-width: 0.45;              /* user units — ~2px at desktop size */
  stroke-dasharray: 1.6 2.4;
  animation: orbit-drift 42s linear infinite;
}
@keyframes orbit-drift { to { stroke-dashoffset: -360; } }
.portrait__badge {
  position: absolute;
  bottom: 4%;
  inset-inline-start: -4%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-radius: var(--shape-full);
  background: var(--surface-container-highest);
  color: var(--on-surface);
  box-shadow: var(--elev-2);
  font-size: var(--type-label-lg);
  font-weight: 700;
}
.portrait__badge .dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: #2e7d32;
  box-shadow: 0 0 0 0 rgb(46 125 50 / 0.6);
  animation: pulse 2.4s var(--ease-standard) infinite;
}
@keyframes spin  { to { transform: rotate(360deg); } }
@keyframes pulse {
  70%  { box-shadow: 0 0 0 12px rgb(46 125 50 / 0); }
  100% { box-shadow: 0 0 0 0  rgb(46 125 50 / 0); }
}

/* --------------------------------------------------------------------------
   Grids
   -------------------------------------------------------------------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: 16px;
}
.stat-card { gap: 6px; }
.stat-card__value { font-size: var(--type-headline-lg); font-weight: 800; letter-spacing: -0.03em; }

.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
.about-copy { display: flex; flex-direction: column; gap: 20px; }

.skill-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 20px;
}
.skill { display: flex; flex-direction: column; gap: 10px; }
.skill__head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.skill__dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-inline-end: 10px;
  border-radius: 50%;
  background: var(--lang, var(--primary));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--lang, var(--primary)) 22%, transparent);
}

/* --------------------------------------------------------------------------
   Setup — devices, specs, operating systems
   -------------------------------------------------------------------------- */
.setup-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}
.device-card { position: relative; gap: 18px; align-items: center; text-align: center; }
.device-card--wide { grid-column: span 2; }

.device-card__tag {
  align-self: flex-start;
  padding: 6px 14px;
  border-radius: var(--shape-full);
  background: var(--primary-container);
  color: var(--on-primary-container);
  font-size: var(--type-label-md);
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.device-card__tag--alt { background: var(--tertiary-container); color: var(--on-tertiary-container); }

.device-card__art {
  display: grid;
  place-items: center;
  width: 100%;
  padding: 12px 0;
}
.device-card__art .device { height: clamp(180px, 26vw, 230px); width: auto; }
.device-card__art--wide {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* Laptop pinned top, terminal pinned bottom. Leftover height goes in the
     middle — it must never be absorbed by stretching the terminal. */
  justify-content: space-between;
  gap: 22px;
  padding: 0;
}

/* --------------------------------------------------------------------------
   Terminal — spans the full laptop card, below the art and specs.
   Stays dark in both themes on purpose (a terminal is a terminal), but sits on
   the palette's warm neutrals rather than a stock editor's cool navy, so it
   reads as part of the page instead of a pasted screenshot.
   -------------------------------------------------------------------------- */
/* Boot screen on the laptop. The screen rect is dark in both themes, so these
   stay light rather than following a colour role. */
.boot { color: #e8e2df; }        /* one knob for the whole boot screen */
.boot__logo { fill: currentColor; }
.boot__spinner { fill: currentColor; }

/* Each dot laps the full circle on an ease-in-out curve, staggered by delay.
   Because they're all easing at different phases they bunch up on one side and
   stretch out on the other — that chase is what a linear ring of dots lacks.
   view-box keeps the origin in SVG coordinates, so they orbit the spinner's
   centre rather than swinging around the frame's top-left. */
.boot__dot {
  transform-box: view-box;
  transform-origin: 135px 88px;
  animation: boot-orbit 1.9s cubic-bezier(0.58, 0.12, 0.42, 0.88) infinite;
}
.boot__dot:nth-child(2) { animation-delay: -0.13s; }
.boot__dot:nth-child(3) { animation-delay: -0.26s; }
.boot__dot:nth-child(4) { animation-delay: -0.39s; }
.boot__dot:nth-child(5) { animation-delay: -0.52s; }

@keyframes boot-orbit {
  to { transform: rotate(360deg); }
}

/* Laptop sits above, terminal below — two separate blocks in the art column. */
.laptop__frame {
  display: block;
  width: 100%;
  max-width: 360px;
  height: auto;
  margin-inline: auto;
}

.terminal {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;      /* never grows — the body height below is a hard cap */
  width: 100%;
  border-radius: var(--shape-md);
  overflow: hidden;
  background: #140c0b;
  box-shadow: inset 0 0 0 1px #2a2320, var(--elev-2);
}

.terminal__bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  background: #201a18;
  border-bottom: 1px solid #2a2320;
}
.terminal__dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
}
.terminal__dot--red    { background: #ff5f57; }
.terminal__dot--yellow { background: #febc2e; }
.terminal__dot--green  { background: #28c840; }
.terminal__title {
  flex: 1;
  margin-inline-end: 46px;
  text-align: center;
  font-family: var(--font-plain);
  font-size: var(--type-label-md);
  font-weight: 600;
  color: #8a7d78;
}

/* Fixed height: the script grows to ~27 lines, so an auto-height box would
   expand then snap back on every loop and shove the page around. */
.terminal__body {
  height: clamp(200px, 22vw, 268px);   /* hard cap — content scrolls inside */
  padding: 16px 18px;
  margin: 0;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  font-family: "SF Mono", "JetBrains Mono", "Fira Code", ui-monospace, monospace;
  font-size: clamp(11px, 1.05vw, 13px);
  line-height: 1.65;
  color: #ddd3cf;
  text-align: start;
  white-space: pre;
}
.terminal__body::-webkit-scrollbar { display: none; }
.terminal__line { min-height: 1.65em; }

.terminal__body .t-prompt  { color: #ff8a66; }   /* coral, matches the brand */
.terminal__body .t-cmd     { color: #c792ea; }
.terminal__body .t-keyword { color: #c792ea; }
.terminal__body .t-fn      { color: #82aaff; }
.terminal__body .t-string  { color: #c3e88d; }
.terminal__body .t-comment { color: #7a6d68; }
.terminal__body .t-number  { color: #f78c6c; }
.terminal__body .t-var     { color: #ffcb6b; }
.terminal__body .t-type    { color: #f07178; }
.terminal__body .t-success { color: #9ece6a; }
.terminal__body .t-task    { color: #89ddff; }
.terminal__body .t-dim     { color: #7a6d68; }

.terminal__cursor {
  display: inline-block;
  width: 0.55em;
  height: 1.05em;
  background: #ff8a66;
  vertical-align: text-bottom;
  animation: cursor-blink 1s step-end infinite;
}
@keyframes cursor-blink {
  50% { opacity: 0; }
}

/* Device illustration parts — all theme-aware */
.device__body     { fill: var(--surface-container-highest); }
.device__panel    { fill: var(--primary-container); }
.device__visor    { fill: var(--surface-variant); }
.device__screen   { fill: var(--n-10); }
.device__base     { fill: var(--surface-container-high); stroke: var(--outline); stroke-width: 2; stroke-linejoin: round; }
.device__lens     { fill: var(--n-12); }
.device__glint    { fill: var(--nv-60); }
.device__flash,
.device__flash-box{ fill: var(--tertiary); }
.device__seam,
.device__seam-bar { stroke: var(--outline-variant); stroke-width: 2; fill: var(--outline-variant); }
.device__seam     { fill: none; }
.device__outline  { fill: none; stroke: var(--outline); stroke-width: 2; }
[data-theme="dark"] .device__screen { fill: var(--n-4); }

.device-card--wide {
  display: grid;
  /* Art column takes the larger share — it now holds a working screen, not
     just an icon, and the extra width is what makes the terminal legible. */
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  /* stretch, not center: centring left dead space above the specs title and
     below the terminal. Both columns now run the full height of the row. */
  align-items: stretch;
  text-align: start;
  gap: 24px;
}
.device-card--wide .device-card__tag { grid-column: 1 / -1; }
.device-card__specs { display: flex; flex-direction: column; gap: 14px; }

.spec-list { display: flex; flex-direction: column; gap: 12px; list-style: none; }
.spec-list li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 12px 14px;
  border-radius: var(--shape-md);
  background: var(--surface-container);
  font-size: var(--type-body-md);
  line-height: 1.4;
}
.spec-list .m3-icon { color: var(--primary); flex: 0 0 auto; }


/* --------------------------------------------------------------------------
   Connect — link cards
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Availability call to action
   -------------------------------------------------------------------------- */
.cta-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  gap: clamp(24px, 4vw, 44px);
  align-items: center;
  margin-bottom: 36px;
  padding: clamp(24px, 4vw, 40px);
  border-radius: var(--shape-xxl);
  background: linear-gradient(135deg, var(--primary-container), var(--tertiary-container));
  color: var(--on-primary-container);
}
.cta-card__head { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.cta-card__head .body-lg { color: color-mix(in srgb, var(--on-primary-container) 82%, transparent); }
.cta-card__badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px 8px 14px;
  border-radius: var(--shape-full);
  background: var(--surface-container-lowest);
  color: var(--on-surface);
  font-size: var(--type-label-md);
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.cta-card__badge .dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #2e7d32;
  box-shadow: 0 0 0 0 rgb(46 125 50 / 0.6);
  animation: pulse 2.4s var(--ease-standard) infinite;
}
.cta-card__services {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  list-style: none;
}
.cta-card__services li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border-radius: var(--shape-lg);
  background: color-mix(in srgb, var(--surface-container-lowest) 70%, transparent);
  font-size: var(--type-body-sm);
  line-height: 1.4;
  color: var(--on-surface-variant);
}
.cta-card__services b { font-size: var(--type-body-md); color: var(--on-surface); }
.cta-card__services .m3-icon { font-size: 22px; color: var(--primary); flex: 0 0 auto; }
.cta-card__actions {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.cta-card__actions .m3-button--outlined {
  color: var(--on-primary-container);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-primary-container) 40%, transparent);
}

.link-grid__label {
  margin-bottom: 14px;
  font-size: var(--type-label-md);
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

/* Two even columns — six cards fill three tidy rows. */
.link-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}
.link-card {
  flex-direction: row;
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  text-align: start;
  border-width: 0;
  font-family: inherit;
  cursor: pointer;
}

.link-card__text {
  display: flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
}
.link-card__title {
  font-size: var(--type-title-md);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--on-surface);
}
.link-card__meta {
  font-size: var(--type-body-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--primary);
  overflow: hidden;
  text-overflow: ellipsis;
}
.link-card__desc {
  font-size: var(--type-label-md);
  line-height: 1.35;
  color: var(--on-surface-variant);
}
.link-card__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: var(--shape-lg);
  background: var(--primary-container);
  color: var(--on-primary-container);
  transition: border-radius var(--dur-long-1) var(--spring-bouncy);
}
.link-card__icon--tertiary { background: var(--tertiary-container); color: var(--on-tertiary-container); }
.link-card__icon--secondary{ background: var(--secondary-container); color: var(--on-secondary-container); }
.link-card__icon--muted    { background: var(--surface-container-highest); color: var(--on-surface-variant); }
/* Link cards are short rows — the card default of 48px would round them into a
   full pill, which reads differently from the tall project cards. Soften less. */
.m3-card.m3-interactive.link-card:hover { border-radius: var(--shape-xl-increased); transform: translateY(-4px); }
.link-card:hover .link-card__icon { border-radius: 26px; }
.link-card__arrow {
  flex: 0 0 auto;
  align-self: center;
  color: var(--on-surface-variant);
  transition: transform var(--dur-medium-2) var(--spring-bouncy);
}
.link-card:hover .link-card__arrow { transform: translate(4px, -4px); }

/* --------------------------------------------------------------------------
   Roles
   -------------------------------------------------------------------------- */
.role-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
}
.role-strip li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px 8px 12px;
  border-radius: var(--shape-full);
  background: var(--surface-container-high);
  color: var(--on-surface-variant);
  font-size: var(--type-label-lg);
  font-weight: 600;
  white-space: nowrap;
}
.role-strip .m3-icon { font-size: 18px; color: var(--primary); }

.role-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  gap: 12px;
}
.role-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px;
  border-radius: var(--shape-lg);
  background: var(--surface-container);
  overflow: hidden;
  transition: border-radius var(--dur-long-1) var(--spring-bouncy),
              background    var(--dur-medium-2) var(--ease-standard);
}
.role-card--link { cursor: pointer; }
.role-card--link:hover { background: var(--surface-container-high); border-radius: var(--shape-xl); }
.role-card__icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-bottom: 4px;
  border-radius: var(--shape-md);
  background: var(--primary-container);
  color: var(--on-primary-container);
  transition: border-radius var(--dur-long-1) var(--spring-bouncy);
}
.role-card--link:hover .role-card__icon { border-radius: 20px; }
.role-card__icon--tertiary { background: var(--tertiary-container); color: var(--on-tertiary-container); }
.role-card__icon--secondary{ background: var(--secondary-container); color: var(--on-secondary-container); }
.role-card__icon--muted    { background: var(--surface-container-highest); color: var(--on-surface-variant); }
.role-card__out {
  font-size: 16px;
  margin-inline-start: 6px;
  vertical-align: -2px;
  color: var(--primary);
  transition: transform var(--dur-medium-2) var(--spring-bouncy);
  display: inline-block;
}
.role-card--link:hover .role-card__out { transform: translate(3px, -3px); }

/* --------------------------------------------------------------------------
   OS strip — lives inside the laptop card
   -------------------------------------------------------------------------- */
.os-strip {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 6px;
  padding-top: 18px;
  border-top: 1px solid var(--outline-variant);
}
.os-strip__label {
  font-size: var(--type-label-md);
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}
.os-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  border-radius: var(--shape-md);
  background: var(--surface-container);
}
.os-row__icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: var(--shape-sm);
  background: var(--primary-container);
  color: var(--on-primary-container);
}
.os-row__icon--alt       { background: var(--tertiary-container);  color: var(--on-tertiary-container); }
.os-row__icon--secondary { background: var(--secondary-container); color: var(--on-secondary-container); }

.project-grid {
  display: grid;
  /* min(320px, 100%), not a bare 320px. A bare minimum is a *floor* the track
     cannot go below, so on a viewport narrower than 320px the column stayed
     320px wide and pushed the page sideways. The min() lets the track collapse
     to the container once the container is the smaller of the two. */
  grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
  gap: 20px;
}
.project-card {
  gap: 16px;
  transition:
    border-radius var(--dur-long-1) var(--spring-bouncy),
    box-shadow    var(--dur-medium-2) var(--ease-standard),
    transform     var(--dur-long-1) var(--spring-default),
    opacity       var(--dur-medium-3) var(--ease-standard);
}
.project-card[hidden] { display: none; }
.project-card.is-out { opacity: 0; transform: scale(0.9); }
a.project-card { text-decoration: none; color: inherit; }
.project-card__media {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  border-radius: var(--shape-lg-increased);
  background: linear-gradient(135deg, var(--primary-container), var(--tertiary-container));
  color: var(--on-primary-container);
  transition: border-radius var(--dur-long-1) var(--spring-bouncy);
}
.project-card__badge {
  position: absolute;
  top: 12px;
  inset-inline-start: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 9px;
  border-radius: var(--shape-full);
  background: var(--surface-container-lowest);
  color: var(--on-surface);
  box-shadow: var(--elev-1);
  font-size: var(--type-label-md);
  font-weight: 700;
}
.project-card__badge .m3-icon { font-size: 15px; color: var(--primary); }

/* Status badges for the apps that aren't on the Play Store yet. Same pill, so a
   mixed grid still reads as one row of cards — only the icon colour and the
   label separate them. "Paused" is deliberately the quietest of the three. */
.project-card__badge--dev .m3-icon { color: var(--tertiary); }
.project-card__badge--paused,
.project-card__badge--paused .m3-icon { color: var(--on-surface-variant); }
/* Sized in em so it tracks the badge label. No fill/color here — the mark
   keeps its own four brand colours. */
.play-mark {
  width: 1.05em;
  height: 1.05em;
  flex: 0 0 auto;
}

/* App launcher icon, sitting on the media plate like a Play Store listing. */
.app-icon {
  width: clamp(84px, 22%, 104px);
  height: auto;
  aspect-ratio: 1;
  border-radius: 24%;                 /* close to Android's adaptive squircle */
  box-shadow: var(--elev-3);
  transition: transform var(--dur-long-1) var(--spring-bouncy),
              border-radius var(--dur-long-1) var(--spring-bouncy);
}
.m3-card.m3-interactive:hover .app-icon { transform: scale(1.08); border-radius: 34%; }

.app-native {
  margin-top: 2px;
  font-size: var(--type-body-md);
  font-weight: 600;
  color: var(--primary);
}

.app-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: -4px;
  font-size: var(--type-label-md);
  font-weight: 600;
  color: var(--on-surface-variant);
}
.app-meta > span { display: inline-flex; align-items: center; gap: 6px; }
.app-meta .m3-icon { font-size: 16px; }
.project-card__media .m3-icon {
  font-size: 44px;
  transition: transform var(--dur-long-1) var(--spring-bouncy);
}
/* Concentric with the card: outer 48 − 24px padding = 24 inner. */
.m3-card.m3-interactive:hover .project-card__media { border-radius: var(--shape-xl); }
.m3-card.m3-interactive:hover .project-card__media .m3-icon { transform: scale(1.12); }
.project-card__title { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.project-card__arrow { transition: transform var(--dur-medium-2) var(--spring-bouncy); }
.m3-card.m3-interactive:hover .project-card__arrow { transform: translate(4px, -4px); }

/* Contact */
.footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-block: 28px 40px;
  border-top: 1px solid var(--outline-variant);
  color: var(--on-surface-variant);
  font-size: var(--type-body-md);
}
.footer__links { display: flex; gap: 4px; }

/* --------------------------------------------------------------------------
   Scroll reveal
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity   var(--dur-long-2) var(--ease-standard),
    transform var(--dur-xlong-1) var(--spring-slow);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; }
  .hero__portrait { order: -1; max-width: 300px; }
  .about-grid { grid-template-columns: 1fr; }
  .device-card--wide { grid-template-columns: 1fr; text-align: center; }
  .device-card--wide .device-card__specs { text-align: start; }
}

@media (max-width: 900px) {
  .cta-card { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .setup-grid { grid-template-columns: 1fr; }
  .device-card--wide { grid-column: span 1; }
  .link-grid { grid-template-columns: 1fr; }
  .link-card { padding: 16px 18px; gap: 14px; }
  .cta-card__services { grid-template-columns: 1fr; }
  .cta-card__actions .m3-button { flex: 1 1 100%; }
}

@media (max-width: 480px) {
  /* The given name stays visible — the wordmark reads "Islam AlorabI" at every
     width. Shrinking the type and the trailing padding is what buys the room;
     hiding half the name is not. */
  .brand { padding-inline-end: 8px; }
  .brand__name { font-size: var(--type-title-md); }
}

/* Below ~360px the large CTA buttons stop fitting. `.m3-button` is
   `white-space: nowrap`, so "Message me on Telegram" at size-l could not wrap
   and forced the CTA card 29px wider than the screen. Step them down to the
   default button metrics rather than letting the label wrap — a wrapped pill
   looks broken, a slightly smaller one does not. */
@media (max-width: 380px) {
  /* The orbit ring sits at inset:-6%, so the portrait plus its ring needs
     112% of the portrait's width. Above ~368px the 300px max-width caps it
     well inside the shell; below that the shell is the binding constraint and
     the ring pokes out. Give the portrait itself the 12% back. */
  .hero__portrait { width: 88%; }

  .m3-button.size-l {
    --pill: 22px;
    min-height: 44px;
    padding: 0 18px;
    font-size: var(--type-label-lg);
  }
}

@media (max-width: 700px) {
  .m3-top-bar__nav { display: none; }

  /* The auto margin that pushes the right-hand group over lives on the nav,
     which is hidden at this width — so without this the toggle collapses
     against the wordmark instead of sitting at the far edge. */
  #theme-toggle { margin-inline-start: auto; }
  .m3-nav-bar { display: flex; }
  .fab-dock { bottom: 96px; right: 16px; }
  /* Clear the fixed bottom nav bar, which only exists at this width. */
  .footer { padding-bottom: 120px; }
  html { scroll-padding-top: 80px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }

  /* The dots all start at 12 o'clock and are separated only by animation
     delay, so freezing the animation collapses them into one dot. Space them
     statically instead. */
  .boot__dot:nth-child(2) { transform: rotate(72deg); }
  .boot__dot:nth-child(3) { transform: rotate(144deg); }
  .boot__dot:nth-child(4) { transform: rotate(216deg); }
  .boot__dot:nth-child(5) { transform: rotate(288deg); }
}
