/* ============================================================
   Flóki Sigurjónsson — portfolio
   White ground, gray type (black on hover), all lowercase.
   ============================================================ */

/* ---- typography & spacing knobs ------------------------------
   --font     the typeface for the whole site
   --type     the base text size (everything scales from it)
   --gap-y/x  the archive grid gaps; --gap-y also sets the space
              between the CV block and the works grid

   The site runs on a self-hosted face (fonts/ folder). To swap it:
   drop the new file in fonts/, point the @font-face src at it
   (format("opentype") for .otf, "truetype" .ttf, "woff2" .woff2),
   and put its family name first in --font.
--------------------------------------------------------------- */

@font-face {
  font-family: "Berthold Standard BQ";
  src: url("../fonts/BertholdStandardBQ-Medium.otf") format("opentype");
  font-weight: normal;
  font-style: normal;
  /* block: never paint in the fallback face — a swap would shift
     every scramble-locked word box when the real metrics arrive */
  font-display: block;
}

:root {
  --bg: #fff;
  --ink: #b8b8b8;          /* base text: light gray */
  --ink-hover: #000;       /* links & hovered items turn black */
  --ink-dim: #d2d2d2;      /* inactive / secondary */
  --ink-faint: #e6e6e6;
  --pt-red: #e0001b;       /* perpetual type red (its loader screen) */
  --font: "Berthold Standard BQ", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --type: 24px;
  --pad: 10px;
  --gap-y: 28px;
  --col-gap: 24px;   /* shared by the CV columns and the archive grid */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* taps are invisible interactions everywhere — no gray flash on
   touch screens; the hover / morph states are the feedback */
html {
  -webkit-tap-highlight-color: transparent;
  /* no double-tap zoom: tapping fast through the slideshow would
     otherwise zoom the page. Pinch-to-zoom still works. */
  touch-action: manipulation;
}

/* hand-placed mobile line breaks: write <br class="m"> in the
   data.js text — it breaks the line on phones only. (To keep two
   words together at every width, join them with &nbsp; instead.) */
br.m { display: none; }
@media (max-width: 560px) {
  br.m { display: inline; }
}

html, body {
  height: 100%;
  overscroll-behavior: none; /* no rubber-band bounce at the ends */
  /* stray horizontal overflow (widened caption boxes, morph
     overhang at the edges) must never cause sideways scrolling */
  overflow-x: clip;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--type);
  line-height: 1.1;  /* type size plus 10% */
  text-transform: lowercase;
  letter-spacing: 0.05em;
  /* printed-ink effect: a faint blurred black copy beneath every
     glyph — it follows the text through hover morphs and color
     changes, like ink bleeding through the sheet */
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
  /* never fake bold/italic from the single Medium file */
  font-synthesis: none;
}

a, .linklike {
  color: var(--ink);
  text-decoration: none;
  background: none;
  border: 0;
  font: inherit;
  /* buttons ship with text-shadow: none, letter-spacing: normal and
     text-transform: none in the UA stylesheet, which broke the ink
     effect and made the home "index" render narrower than on other
     pages — force all of them back to inheriting */
  text-shadow: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
a:hover, .linklike:hover { color: var(--ink-hover); }
/* links into perpetual type wear the program's red — the ink
   bleed beneath them too — and go back to black ink on hover */
a.pt-link {
  color: var(--pt-red);
  text-shadow: 0 0 2px rgba(224, 0, 27, 0.3);
}
a.pt-link:hover {
  color: var(--ink-hover);
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
}

::selection { background: #000; color: #fff; }

/* text sits normal at rest and scrambles to random letter sizes
   (scramble.js sets --s per letter) while hovered. Words are
   inline-blocks locked to their normal width, so the scramble
   never re-wraps a line. */
.rw {
  display: inline-block;
  /* letters grown by the morph must overflow the fixed word box,
     never wrap inside it (that splits the word onto a new line) */
  white-space: nowrap;
}
.rs {
  transition: font-size 0.15s ease;
  /* zero line-height: the spans never stretch the line box, so
     every line keeps the base rhythm; big letters just overhang */
  line-height: 0;
}
/* whole text blocks are the hover unit (matching the box-lock in
   scramble.js) — inline fragments never scramble on their own */
a:hover .rs, .linklike:hover .rs, p:hover .rs, li:hover .rs,
h2:hover .rs, figure:hover .rs, figcaption:hover .rs {
  font-size: var(--s);
}
/* ---- top bar ------------------------------------------------ */

.bar {
  position: fixed;  /* stays put while the page scrolls */
  top: 0; left: 0; right: 0;
  display: flex;
  justify-content: space-between;
  padding: var(--pad);
  z-index: 30;
}
.bar nav { display: flex; gap: 18px; }
/* nav items never break internally — "perpetual type" is one unit */
.bar a, .bar .linklike { white-space: nowrap; }

/* ---- home: slideshow ---------------------------------------- */

body.home { overflow: hidden; }

#stage {
  position: fixed;
  inset: 0;
}

/* index-overlay layout: the image sits in the lower-right corner
   on the page margins, the title in the lower-left */
#slide, #slide-video {
  position: absolute;
  right: var(--pad);
  bottom: var(--pad);
  width: 50vw;
  height: auto;
  max-height: 80vh;
  object-fit: contain;
  object-position: bottom right;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
  /* clicks pass through the image to the prev/next hit zones
     beneath, so stepping never opens a project by accident — the
     title caption is the only way into the project page */
  z-index: 15;
  pointer-events: none;
}

/* invisible prev/next hit zones over the whole screen */
.hit {
  position: absolute;
  top: 0; bottom: 0;
  width: 50%;
  background: none;
  border: 0;
  z-index: 10;
  /* stepping the slideshow is an invisible interaction: no gray
     tap flash on touch screens */
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
/* thin drawn arrows: left half of the screen steps back, right
   half steps forward. Styled like the type — site gray with the
   printed-ink effect (a faint blurred black copy beneath). */
.hit-prev {
  left: 0;
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cdefs%3E%3Cfilter id='b' x='-50%25' y='-50%25' width='200%25' height='200%25'%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3C/filter%3E%3C/defs%3E%3Cpath d='M26 16H6M13 9l-7 7 7 7' fill='none' stroke='%23000' stroke-opacity='0.3' stroke-width='3.5' filter='url(%23b)'/%3E%3Cpath d='M26 16H6M13 9l-7 7 7 7' fill='none' stroke='%23b8b8b8' stroke-width='3.5'/%3E%3C/svg%3E") 16 16, w-resize;
}
.hit-next {
  right: 0;
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cdefs%3E%3Cfilter id='b' x='-50%25' y='-50%25' width='200%25' height='200%25'%3E%3CfeGaussianBlur stdDeviation='1'/%3E%3C/filter%3E%3C/defs%3E%3Cpath d='M6 16h20M19 9l7 7-7 7' fill='none' stroke='%23000' stroke-opacity='0.3' stroke-width='3.5' filter='url(%23b)'/%3E%3Cpath d='M6 16h20M19 9l7 7-7 7' fill='none' stroke='%23b8b8b8' stroke-width='3.5'/%3E%3C/svg%3E") 16 16, e-resize;
}

.caption {
  position: fixed;
  left: var(--pad);
  right: 50vw;
  bottom: var(--pad);
  text-align: left;
  z-index: 20;
  pointer-events: none;
  white-space: pre-line;
}
.caption .note { display: block; }
/* the text itself is hoverable (scrambles on hover); the rest of
   the strip stays transparent to clicks. Right padding widens the
   box so letters extended by the morph stay inside it. */
.caption .cap {
  pointer-events: auto;
  display: inline-block;
  padding: 0 1.2em 0 0;
}
.caption .cap:hover .rs { font-size: var(--s); }

/* ---- home: index overlay ------------------------------------ */

#index-panel {
  position: fixed;
  inset: 0;
  z-index: 25;
  background: var(--bg);
  padding: 44px var(--pad) var(--pad);
  overflow-y: auto;
}
/* three shared columns — number, title, tag — so every tag sits
   on the same vertical line just right of the longest title */
#index-list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, max-content);
}
#index-list li { display: contents; }
#index-list a {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}
#index-list .num { min-width: 2.4em; }
#index-list .tag {
  color: var(--ink); /* same gray as the titles */
  margin-left: 2.4em;
}
/* the whole row is the hover unit: the category goes black with it */
#index-list a:hover .tag { color: var(--ink-hover); }
#index-list .meta { color: var(--ink-dim); }

/* hover preview: a random image from the hovered project,
   anchored bottom-right on the same page margins as everything */
#index-preview, #index-preview-video {
  position: fixed;
  right: var(--pad);
  bottom: var(--pad);
  width: 50vw;
  height: auto;
  /* tall media must not run out of the viewport: cap the height
     and keep the picture pinned to the bottom-right corner */
  max-height: 80vh;
  object-fit: contain;
  object-position: bottom right;
  display: none;
  pointer-events: none;
  z-index: 26;
}
#index-preview.on, #index-preview-video.on { display: block; }

/* ---- info page ---------------------------------------------- */

body.info { padding: 0 var(--pad) var(--pad); }

.cv {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--col-gap);
  padding-top: 44px;
  align-content: start;
}
.cv h2 { font-size: inherit; font-weight: normal; }
.cv .block { margin-bottom: 1.4em; }
.cv .role { margin-bottom: 1.4em; }
.cv .contact { text-align: right; }
/* "— " lines: dash in its own column, a word space after it, and
   the text (wrapped lines included) aligned in the second column */
.cv .hang {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.3em;
}
/* this column sits on the page's right edge: align letters to the
   right INSIDE their fixed word boxes, so morph-grown letters
   spill left into the page instead of right out of view. At rest
   content exactly fills the box, so this changes nothing visually. */
.cv .contact .rw { text-align: right; }
/* email and instagram sit on consecutive lines, no gap between */
.cv .contact .line { margin-bottom: 0; }
/* recognition entries: one blank line between each */
.cv .contact .block p + p { margin-top: 1.1em; }
.cv .aside { color: var(--ink-dim); }

/* ---- archive grid ------------------------------------------- */

.archive {
  /* 3 flowing (masonry) columns aligned with the CV columns above:
     items stack with the same spacing vertically as horizontally,
     so rows don't line up — the columns just flow */
  columns: 3;
  column-gap: var(--col-gap);
  column-fill: balance;  /* columns always start flush at the top */
  margin-top: var(--col-gap);
}
.archive figure {
  cursor: pointer;
  break-inside: avoid;
  /* padding, not margin: margins can bleed across column breaks
     (Safari) and knock the next column's first item out of line */
  padding-bottom: var(--col-gap);
}
.archive img,
.archive video {
  width: 100%;
  height: auto;
  display: block;
  margin-bottom: 6px;
}
/* grid media fades in once decoded instead of popping in */
.archive .fade { opacity: 0; transition: opacity 0.4s ease; }
.archive .fade.ld { opacity: 1; }
.archive figcaption {
  color: var(--ink-dim);
  white-space: pre-line;
  /* wider box on the sides so morph-extended letters stay inside;
     the negative margin keeps the text flush with the image edge */
  padding: 0 1.2em;
  margin: 0 -1.2em;
}
.archive figure:hover figcaption { color: var(--ink-hover); }

/* ---- project sub-page --------------------------------------- */

body.project { padding: 0 var(--pad) var(--pad); }

.project-page {
  padding-top: 44px;
  /* last image clears the fixed prev/next bar at the bottom */
  padding-bottom: calc(var(--gap-y) * 2);
}
/* text column pinned on the left under the header; the images
   scroll beside it on the right, sized like the index preview */
.project-head {
  position: fixed;
  top: 44px;
  left: var(--pad);
  width: calc(46vw - 2 * var(--pad));
}
.project-head .psummary { margin-top: 1.4em; }
.project-head .plink { margin-top: 0; } /* directly under the title */
.project-head .pcredit { margin-top: 0; } /* credit joins the same block */
.project-head .ptry { margin-top: 0; } /* directly under the description */

#project-images {
  width: 50vw;
  margin-left: auto; /* hugs the right page margin */
}
#project-images figure { margin-bottom: var(--gap-y); }
#project-images img,
#project-images video {
  width: 100%;
  height: auto;
  /* tall media stays within one viewport; object-fit keeps the
     aspect ratio, centred in the full-width box */
  max-height: 86vh;
  object-fit: contain;
  display: block;
}
#project-images figcaption {
  color: var(--ink-dim);
  margin-top: 6px;
}

/* pinned to the viewport bottom, mirroring the fixed header, so
   prev/next sit in the same spot on every sub-page — scrolled,
   scrolling, or too short to scroll */
.project-nav {
  position: fixed;
  left: 0; right: 0;
  bottom: 0;
  display: flex;
  justify-content: space-between;
  padding: 0 var(--pad) var(--pad);
  z-index: 30;
}
/* the next-project link sits flush on the right margin; letters
   grown by the morph spill left into the page, not out of view
   (same trick as the contact column) */
.project-nav a:last-child { text-align: right; }
.project-nav a:last-child .rw { text-align: right; }

/* ---- app page (perpetual type embed) ------------------------ */

body.app { overflow: hidden; }

/* the window and its loading skeleton share one box: a floating
   window centred in the space under the header bar, rounded, with
   a hairline stroke. The box is FIXED at the app's design size and
   zoomed as one unit (--app-scale, set by the page script), so the
   app inside never reflows, crops or squeezes — it just scales. */
.app-frame, .app-skeleton {
  position: fixed;
  top: calc(50% + 22px);
  left: 50%;
  width: 1680px;
  height: 1020px;
  transform: translate(-50%, -50%) scale(var(--app-scale, 1));
  border: 1px solid var(--ink-faint);
  border-radius: 14px;
}

.app-frame { background: #fff; }

/* skeleton: covers the window until the app has loaded — a solid
   screen of the program's red with its star loader spinning in
   white at the centre, so the frame reads as interactive arriving */
.app-skeleton {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--pt-red);
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.app-skeleton.done { opacity: 0; }

.app-loader {
  width: 72px;
  height: 72px;
  animation: loader-spin 1s linear infinite;
}

@keyframes loader-spin {
  to { transform: rotate(360deg); }
}

/* ---- smear backgrounds (experiment, drawn by js/smear.js) ----
   images pull their edge pixels out to fill the space around them;
   type above sits in difference blend mode to stay readable */

#smear-bg {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
/* one document-level layer holds every grid smear canvas, in page
   coordinates, behind all content (kept out of the multicol flow) */
#smear-layer {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
}
canvas.smear {
  position: absolute;
  pointer-events: none;
}

/* the bar stops painting its white strip — the smear runs under it
   and the difference blend keeps the text legible instead */
.smearfx .bar { background: transparent; }

.smearfx .bar,
.smearfx .caption,
.smearfx .project-head,
.smearfx .project-nav,
.smearfx .archive figcaption {
  mix-blend-mode: difference;
}
.smearfx .bar a,
.smearfx .bar .linklike,
.smearfx .caption,
.smearfx .project-head,
.smearfx .project-nav a,
.smearfx .archive figcaption {
  color: #fff;
}
/* black would vanish under difference (black changes nothing), so
   hovers dim to the site gray instead */
.smearfx .bar a:hover,
.smearfx .bar .linklike:hover,
.smearfx .project-nav a:hover,
.smearfx .archive figure:hover figcaption {
  color: var(--ink);
}

/* ---- small screens ------------------------------------------ */

@media (max-width: 900px) {
  .cv { grid-template-columns: 1fr 1fr; }
  .cv .contact { text-align: left; }
  .archive { columns: 2; }
  /* phones: image nearly full width, lifted one line so the
     lower-left title never sits on top of it */
  #slide, #slide-video {
    width: calc(100vw - 2 * var(--pad));
    max-height: 70vh;
    bottom: calc(var(--pad) + 1.6em);
  }
  /* the title gets the full width so it stays on one line */
  .caption { right: var(--pad); }
  /* sub-pages stack: text above, images full width below */
  .project-head {
    position: static;
    width: auto;
    margin-bottom: var(--gap-y);
  }
  #project-images { width: 100%; }
  /* the first line sits exactly one text line below the header
     bar — bar padding + one line box — matching the body rhythm */
  .project-page { padding-top: calc(var(--pad) + 1.1em); }
  .cv { padding-top: calc(var(--pad) + 1.1em); }
  #index-panel { padding-top: calc(var(--pad) + 1.1em); }
}

@media (max-width: 560px) {
  .cv { grid-template-columns: 1fr; }
  .archive { columns: 1; }

  /* phones: the current project's "title, year" sits under the
     image, mirroring the header — the same --pad from the bottom
     edge as the header has from the top, and the same --pad
     between the text line and the image */
  #slide, #slide-video { bottom: calc(var(--pad) + 1.1em + var(--pad)); }

  /* phones: no hover — the index preview image never shows */
  #index-preview.on, #index-preview-video.on { display: none; }

  /* one-column index: number + title on the first line, the
     category below the title (aligned with it, not the number),
     then a gap before the next project */
  #index-list { display: block; }
  #index-list li { display: block; }
  #index-list a {
    display: grid;
    grid-template-columns: 2.4em auto;
    margin-bottom: 1em;
  }
  #index-list .num { grid-column: 1; }
  #index-list .title { grid-column: 2; }
  #index-list .tag { grid-column: 2; margin-left: 0; }

  /* phones: the name and the three nav links spread evenly across
     the bar (the nav dissolves into the bar's flex row). The type
     never changes size — one size everywhere is the rule. */
  .bar nav { display: contents; }

  /* the floating app window goes near full-bleed on phones,
     anchored below the header */
  .app-frame, .app-skeleton {
    top: 54px;
    left: var(--pad);
    transform: none;
    width: calc(100vw - 2 * var(--pad));
    height: calc(100% - 54px - var(--pad));
  }
}
