/* Zayan Hassan — portfolio
   ---------------------------------------------------------------
   Deliberate choices, noted so future edits keep them:

   - Corners are square. There is no radius token in this file, and no
     shadow token either. Depth comes from 1px hairlines, the way a
     schematic or an instrument face does it.
   - Type is a system serif for prose against a system monospace for
     labels and data. No webfonts, so no font-CDN request and nothing
     from the Inter / Geist / Space Grotesk set.
   - The blue is desaturated and cyan-leaning, closer to a plotted
     oscilloscope trace than to a saturated UI blue. It only ever marks
     links, data, and structure. It is never a gradient and never a fill
     behind a heading.
   - Content sits in one measure centred on the page. Page titles and
     section headings centre their text. Paragraphs run the full measure
     and stay left-aligned, because centred body text is hard to read.
   --------------------------------------------------------------- */

:root {
  --ink: #080a0d;
  --ink-raised: #0e131a;
  --wire: #1e2934;
  --wire-soft: #151d26;

  --text: #e2e8ee;
  --text-dim: #9caab8;
  --text-faint: #6f7e8e;

  --signal: #4fa3e3;
  --signal-bright: #8ac9f6;
  --signal-deep: #0b2134;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --mono: ui-monospace, "Cascadia Mono", "Cascadia Code", Consolas, "SF Mono", Menlo, monospace;

  --sidebar: 14rem;
  --gutter: clamp(1.25rem, 4vw, 4rem);
  --band: clamp(1.5rem, 3vw, 2.5rem);
  --measure: 64rem;
}

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

html {
  background: var(--ink);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--serif);
  font-size: clamp(1rem, 0.28vw + 0.95rem, 1.12rem);
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
}

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

a { color: var(--signal); text-decoration: none; }
a:hover { color: var(--signal-bright); }

:focus-visible {
  outline: 2px solid var(--signal-bright);
  outline-offset: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  padding: 0.6rem 1rem;
  background: var(--signal-deep);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.8rem;
}
.skip:focus { left: 0; }

.u-hide {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------
   Shell and sidebar
   Under 60rem the sidebar collapses into a sticky top bar, because
   hover does not exist on touch and the submenu has to be tappable.
   --------------------------------------------------------------- */

.shell { min-height: 100vh; }

.side {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--ink);
  border-bottom: 1px solid var(--wire);
}

.side__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem var(--gutter);
}

.mark {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--text);
}
.mark:hover { color: var(--text); }

/* The monogram is a stroked SVG, so it reads at the same weight as the name
   beside it. Fixed height, width follows the 28:26 viewBox. */
.mark__logo {
  display: block;
  width: auto;
  height: 1.35rem;
  flex: none;
}

.mark__name {
  display: block;
  font-size: 1.06rem;
  letter-spacing: 0.01em;
}

.burger {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.7rem;
  background: transparent;
  border: 1px solid var(--wire);
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}
.burger:hover { border-color: var(--signal); color: var(--text); }

.burger__box {
  width: 0.85rem;
  height: 0.6rem;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
}

.nav { display: none; padding: 0 var(--gutter) 1.25rem; }
.nav.is-open { display: block; }

.nav__list,
.nav__sub {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__item { border-top: 1px solid var(--wire-soft); }

.nav__row { display: flex; align-items: stretch; }

.nav__link {
  flex: 1 1 auto;
  display: block;
  padding: 0.65rem 0;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
}
.nav__link:hover { color: var(--text); }
.nav__link[aria-current="page"] { color: var(--signal); }

.nav__pip {
  flex: 0 0 auto;
  width: 2.2rem;
  background: transparent;
  border: 0;
  color: var(--text-faint);
  cursor: pointer;
  line-height: 0;
}
.nav__pip:hover { color: var(--signal); }
.nav__pip svg { margin: 0 auto; transition: transform 0.16s ease; }
.nav__item--sub.is-open .nav__pip svg { transform: rotate(90deg); }

.nav__sub {
  display: none;
  padding: 0 0 0.5rem 0.9rem;
  border-left: 1px solid var(--wire);
  margin-left: 0.1rem;
}
.nav__item--sub.is-open > .nav__sub { display: block; }

.nav__sublink {
  display: block;
  padding: 0.34rem 0;
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.03em;
}
.nav__sublink:hover { color: var(--text); }
.nav__sublink[aria-current="page"] { color: var(--signal); }

@media (min-width: 60rem) {
  .shell {
    display: grid;
    grid-template-columns: var(--sidebar) minmax(0, 1fr);
  }

  .side {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    padding: 2.5rem 0 2rem;
    border-bottom: 0;
    border-right: 1px solid var(--wire);
    overflow-y: auto;
  }

  .side__head { display: block; padding: 0 1.5rem 2rem; }
  .burger { display: none; }

  .nav { display: block; padding: 0 1.5rem; }

  /* Desktop reveal. focus-within keeps it reachable by keyboard,
     and the pip button still works for touch-screen laptops. */
  .nav__item--sub:hover > .nav__sub,
  .nav__item--sub:focus-within > .nav__sub { display: block; }
}

/* ---------------------------------------------------------------
   Bands. One measure, centred in the content column.
   --------------------------------------------------------------- */

.main { padding: 0 var(--gutter); }

.band {
  max-width: var(--measure);
  margin: 0 auto;
  padding-top: var(--band);
  padding-bottom: var(--band);
}
.band--open { padding-top: clamp(2rem, 4vw, 3rem); }
.band--tight { padding-bottom: calc(var(--band) * 0.4); }
.band--rule { border-top: 1px solid var(--wire-soft); }

/* Section heading: centred text over a full-measure hairline, with a
   short blue segment centred beneath it. */
.head {
  position: relative;
  margin: 0 0 1.75rem;
  padding-bottom: 0.65rem;
  border-bottom: 1px solid var(--wire);
  font-size: clamp(1.18rem, 1.6vw, 1.45rem);
  font-weight: 400;
  letter-spacing: -0.005em;
  text-align: center;
  color: var(--text);
}
.head::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: -1px;
  width: 3.5rem;
  height: 1px;
  background: var(--signal);
}

/* Page header block */
.title {
  max-width: 30ch;
  margin: 0 auto 0.8rem;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem);
  line-height: 1.14;
  letter-spacing: -0.012em;
  font-weight: 400;
  text-align: center;
}

/* Prose runs the full measure. A 62ch cap is the usual readability advice,
   but Zayan asked for full-width paragraphs rather than a narrow column, so
   line-height carries the legibility instead. */
.prose {
  max-width: none;
  margin: 0;
}
.prose p { margin: 0 0 1.15rem; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { font-weight: 400; color: #fff; }

.prose ul { margin: 0 0 1.15rem; padding-left: 1.2rem; }
.prose li { margin: 0 0 0.5rem; }

.prose a {
  border-bottom: 1px solid var(--signal-deep);
  padding-bottom: 0.05em;
}
.prose a:hover { border-bottom-color: var(--signal); }

/* ---------------------------------------------------------------
   Project cards: image on top, then title, blurb, Read More.
   auto-fit, not auto-fill: empty tracks collapse, so two cards stretch
   to fill the row instead of huddling left. Room for three tracks, so
   adding a third project reflows to thirds with no CSS change.
   --------------------------------------------------------------- */

.cards {
  display: grid;
  gap: clamp(1.25rem, 2.4vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

.card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--wire-soft);
  background: var(--ink-raised);
}
.card:hover { border-color: var(--wire); }

.card__media {
  margin: 0;
  border-bottom: 1px solid var(--wire-soft);
}
/* Cover images share one shape, so cards line up whatever their source image's
   proportions are. */
.card__media img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
/* Screenshots of a full page are taller than 16/9, and everything that
   identifies the site sits at the top. Centring the crop cuts the title off. */
.card__media--top img { object-position: center top; }

.card__body {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  padding: 1.1rem 1.15rem 1.3rem;
}

.card__title {
  margin: 0 0 0.5rem;
  font-size: 1.24rem;
  line-height: 1.24;
  font-weight: 400;
  letter-spacing: -0.008em;
}
.card__title a { color: var(--text); }
.card__title a:hover { color: var(--signal-bright); }

.card__meta {
  margin: 0 0 0.75rem;
  font-family: var(--mono);
  font-size: 0.69rem;
  letter-spacing: 0.05em;
  line-height: 1.55;
  color: var(--text-faint);
}

.card__text {
  flex: 1 1 auto;
  margin: 0;
  font-size: 0.96rem;
  color: var(--text-dim);
}

.btn {
  align-self: flex-start;
  display: inline-block;
  margin-top: 1.1rem;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--wire);
  color: var(--signal);
  font-family: var(--mono);
  font-size: 0.69rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}
.btn:hover {
  border-color: var(--signal);
  background: var(--signal-deep);
  color: var(--signal-bright);
}

.more {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--signal-deep);
  padding-bottom: 0.2rem;
}
.more:hover { border-bottom-color: var(--signal); }
.more span { line-height: 0; }

.center { text-align: center; }

/* ---------------------------------------------------------------
   Document embeds (the paper PDF and the slide deck).
   The frame is hidden below 48rem on purpose: mobile browsers, iOS
   Safari especially, render only the first page of a PDF in an iframe
   and will not scroll it, so small screens get the action bar instead.
   --------------------------------------------------------------- */

/* The frame carries #toolbar=0&navpanes=0&scrollbar=0, which suppresses the
   browser PDF viewer's toolbar, thumbnail rail, and scrollbar, so only the
   document shows. Wheel scrolling still works with the scrollbar hidden, but
   nothing signals that there are more pages, so the Open and Download buttons
   below each embed carry that job. */

/* Hidden below 48rem, where the frames are hidden anyway: an empty bordered box
   with no content in it just reads as a stray line. */
.doc { display: none; }

/* Two stacked frames per viewer. The incoming page loads in the hidden one and
   they cross-fade, so paging does not flash a blank frame. The box carries the
   page proportions because the frames are absolutely positioned and cannot. */
@media (min-width: 48rem) {
  .doc {
    display: block;
    position: relative;
    margin: 0 auto;
    border: 1px solid var(--wire);
    background: var(--ink-raised);
  }

  /* view=Fit scales a whole page into the frame, so one full page shows rather
     than the tail of one and the head of the next. Capped by viewport height so
     a tall page cannot outgrow the screen. */
  .doc--paper { max-width: calc(84vh * 8.5 / 11); aspect-ratio: 8.5 / 11; }
  .doc--slides { max-width: calc(76vh * 16 / 9); aspect-ratio: 16 / 9; }

  .doc__frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    background: var(--ink-raised);
    opacity: 0;
    transition: opacity 0.2s ease;
  }
  .doc__frame.is-active { opacity: 1; }
}

/* Page controls */
.viewer__bar {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 1.1rem;
}
@media (min-width: 48rem) {
  .viewer__bar { display: flex; }
}

.viewer__count {
  margin: 0;
  min-width: 7rem;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}

.viewer__nav { margin-top: 0; cursor: pointer; background: transparent; }
.viewer__nav[disabled] {
  opacity: 0.35;
  cursor: default;
  border-color: var(--wire-soft);
  color: var(--text-faint);
  background: transparent;
}

/* Action buttons sit below the embed, not over it. */
.acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.1rem;
}
.acts .btn { margin-top: 0; }
/* .center only sets text-align, which does nothing to a flex row. */
.acts--center { justify-content: center; }

/* ---------------------------------------------------------------
   Competition results: event, rank and record, awards
   --------------------------------------------------------------- */

.results {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--wire-soft);
}

.results li {
  display: grid;
  gap: 0.2rem 1.5rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--wire-soft);
}

@media (min-width: 48rem) {
  .results li {
    grid-template-columns: minmax(0, 17rem) 8.5rem minmax(0, 1fr);
    align-items: baseline;
  }
}

.results__event { color: var(--text); }

.results__stat {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  color: var(--signal);
}

.results__note { color: var(--text-dim); font-size: 0.95rem; }

/* ---------------------------------------------------------------
   Figures
   --------------------------------------------------------------- */

.shot { margin: 0; }

.shot__img {
  width: 100%;
  border: 1px solid var(--wire);
}

/* ---------------------------------------------------------------
   Video embeds. youtube-nocookie so an visitor is not cookied by
   YouTube unless they actually press play.
   --------------------------------------------------------------- */

.videos {
  display: grid;
  gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.video {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--wire);
  background: var(--ink-raised);
}

/* The imaging test was filmed as a vertical short. */
.video--tall {
  aspect-ratio: 9 / 16;
  max-width: 20rem;
  margin: 0 auto;
}

.video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ---------------------------------------------------------------
   Bottom bar. Negative gutter margins let it span the full content
   column while its inner block stays on the same measure as the page.
   --------------------------------------------------------------- */

.sitefoot {
  margin: var(--band) calc(var(--gutter) * -1) 0;
  padding: 1.9rem var(--gutter) 2.1rem;
  border-top: 1px solid var(--wire);
  background: var(--ink-raised);
}

.sitefoot__inner {
  max-width: var(--measure);
  margin: 0 auto;
  text-align: center;
}

/* Stacked on narrow screens. As a wrapping row, three items break 2 + 1
   and leave the third orphaned on its own line. */
.sitefoot__links {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--mono);
}

@media (min-width: 34rem) {
  .sitefoot__links {
    flex-direction: row;
    gap: 0.9rem 2.75rem;
  }
}

.sitefoot__links li {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 7rem;
}

.sitefoot__key {
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.sitefoot__val {
  font-size: 0.78rem;
  color: var(--text-dim);
  word-break: break-word;
}
.sitefoot__val a { color: var(--signal); }

/* Season attribution under a page title: which programme, which team, my role.
   Same treatment as .card__meta, centred and sitting directly under .title. */
.stamp {
  /* 56ch keeps the patent line on one row; the longer season stamps still
     wrap, which is fine, but a two-line patent number reads like a mistake. */
  max-width: 56ch;
  margin: 0 auto;
  font-family: var(--mono);
  font-size: 0.69rem;
  letter-spacing: 0.05em;
  line-height: 1.7;
  color: var(--text-faint);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* Utilities last, so they win the cascade against component rules. .u-mt once
   sat at the top of this file and was silently beaten by a component's `margin`
   shorthand at equal specificity. Later wins; utilities go last. */

.u-mt { margin-top: 1.75rem; }

/* A figure that opens its own band already has the band's padding above it;
   the extra .u-mt on top of that reads as a hole. Only figures that follow
   text need the gap. */
.band > .shot:first-child { margin-top: 0; }
