/* the signature face — the "Abbie Gale" mark every page's top-left wears */
@font-face {
  font-family: 'Historia Sky Script';
  src: url('fonts/historia-sky-script.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

:root {
  /* ground and ink unified to the home page's palette (2026-09-15) — every
     page sits on the same warm cream and carries the same nav bar */
  --bg: #fffaf5;
  --text: #3e3e42;
  --text-dim: #69645e;
  --sage: #8fa08c;
  /* sage darkened for duty as ink — small text and focus outlines need
     4.5:1 on the cream ground, which the wall colour above can't give */
  --sage-ink: #5f7a5c;
  --card-1: #f1ece5;
  --card-2: #e8e2da;
  --line: rgba(62, 62, 66, 0.10);
  --font: 'Shippori Mincho B1', serif;
  --grtsk: 'grtsk-vf', 'Grtsk Var', 'Grtsk Variable', grtsk-variable, 'TRY Grtsk', 'Hanken Grotesk', 'Inter', system-ui, sans-serif;
  /* the page's outer margin — nav, grid and footer all key off this, so a
     single override changes how inset (and so how zoomed out) the page reads.
     120px at the 1440px reference width — a slightly more zoomed-out read
     than the Figma frame's 90px, per request — scaling down with the
     viewport so narrow screens keep a sensible margin */
  --page-pad: clamp(24px, 6.3vw, 80px);
  /* gutter between the image columns; the nav labels key off it too */
  --col-gap: 16px;
  /* how far "UX UI" sits to the left of "Architecture" */
  --ux-gap: 45px;
}

/* ── Dark option — a deep warm grey instead of the cream ──
   switched on with ?theme=dark in the address bar (remembered across
   pages; ?theme=light returns). The artwork keeps its own paper colours,
   sitting on the dark wall like prints in a dim gallery. */
:root[data-theme="dark"] {
  --bg: #2b2926;
  --text: #ece6df;
  --text-dim: #a8a099;
  --sage: #9db49a;
  --sage-ink: #9db49a;
  --card-1: #3a3733;
  --card-2: #444039;
  --line: rgba(255, 255, 255, 0.10);
}
/* the dark wall carries a fine paper grain, like the artwork's own */
:root[data-theme="dark"] body,
:root[data-theme="dark"] .intro,
:root[data-theme="dark"] .nav {
  background-image: url('img/grain-dark.webp');
}

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

html { scroll-behavior: smooth; }
/* phones must render the sizes as written — iOS Safari otherwise inflates
   text after rotation, which is where "suddenly everything is bigger" comes
   from */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* grtsk-vf carries a slant axis (slnt −15…15). iOS Safari renders such
   variable fonts slanted even at font-style normal — the "everything is
   italic on the iPhone" bug — so the axis is pinned upright for the whole
   tree. Fonts without a slnt axis ignore the setting. */
html { font-variation-settings: 'slnt' 0; }
/* short pages have no scrollbar, which used to hand them ~15px more width
   and shift the right-hand nav links between pages — reserve the gutter
   everywhere so the bar never moves */
html { scrollbar-gutter: stable; }
/* nothing may open a sideways scroll — narrow windows and font-fallback
   moments used to push the bar (and with it the page) past the edge */
html, body { overflow-x: clip; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
ul { list-style: none; }

/* selected text turns the splash orange on a soft tint of the same hue */
::selection { background: rgba(238, 142, 5, 0.16); color: #ee8e05; }

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

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

/* ── Nav ── */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  /* the home page's bar, everywhere: same padding, and the same hairline
     running beneath it throughout — no shadow-on-scroll second line */
  padding: 22px var(--page-pad) 18px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.nav.scrolled { box-shadow: none; }
/* the script mark, identical on every page (home and the case study set
   the same values locally) */
.nav-logo {
  font-family: 'Historia Sky Script', 'Caveat', cursive;
  font-size: 34px;
  letter-spacing: 0;
  line-height: 1;
  margin-left: -0.02em;
  white-space: nowrap;
  color: var(--text);
}
/* the pair sits dead-centre between the script mark and "About": auto
   margins split the bar's free run equally on both sides, at any width
   (2026-09-19 — replacing the old gallery-column anchoring) */
.nav-center {
  display: flex;
  align-items: center;
  gap: var(--ux-gap);
  margin-inline: auto;
  font-family: var(--grtsk);
  font-size: 16px;
  letter-spacing: 0.07em;
  text-transform: capitalize;
  color: var(--text-dim);
  transition: color .25s ease;
  white-space: nowrap;
}
/* "UX UI" leads, keeping the pairing from the Figma file. They are
   separate links so each can highlight its own set of images. */
.nav-center .ux { order: -1; }
.nav-center a { color: var(--text-dim); transition: color .25s ease; }
.nav-center a:hover, .nav-right a:hover { color: #c07c1e; }
.nav-right {
  /* no auto margin — .nav-center's own auto pair does the centring, and a
     third auto margin here would skew the split */
  margin-left: 0;
  display: flex;
  gap: 28px;
  font-family: var(--grtsk);
  font-size: 16px;
  letter-spacing: 0.07em;
  text-transform: capitalize;
}
.nav-right a { color: var(--text-dim); transition: color .25s ease; }
/* the letterforms sit a touch taller than the face draws them */
.nav-center a, .nav-right a { display: inline-block; transform: scaleY(1.08); transform-origin: 50% 55%; }

/* the light/dark switch — a soft-filled pill showing BOTH glyphs, sun left
   and moon right, with a paler rounded segment (the i) sliding behind
   whichever is active. Icons are SVG masks in the nav's inks: the active
   one full text ink, the resting one dimmed, marigold on hover. */
.theme-toggle {
  margin-left: 0;
  flex: none;
  align-self: center;
  position: relative;
  /* optical alignment with the nav labels, set by eye (2026-09-17) */
  top: -3px;
  width: 72px;
  height: 26px;
  box-sizing: border-box;
  border: none;
  border-radius: 13px;
  background: rgba(62, 62, 66, 0.10);
  padding: 0;
  cursor: pointer;
  transition: background .25s ease;
}
:root[data-theme="dark"] .theme-toggle { background: rgba(255, 255, 255, 0.10); }
/* the sliding highlight behind the active icon */
.theme-toggle i {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 33px;
  height: 20px;
  border-radius: 10px;
  background: var(--bg);
  transition: transform .3s cubic-bezier(.2,.7,.3,1);
}
:root[data-theme="dark"] .theme-toggle i { transform: translateX(33px); }
/* the two glyphs, each centred in its half of the pill */
.theme-toggle::before,
.theme-toggle::after {
  content: "";
  position: absolute;
  /* above the sliding highlight, which the ::before otherwise paints under */
  z-index: 1;
  top: 50%;
  margin-top: -6.5px;
  width: 13px;
  height: 13px;
  transition: background .25s ease;
}
.theme-toggle::before {
  /* centred in its half of the pill (segment spans 3–36, centre 19.5) */
  left: 13px;
  background: var(--text);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='4.6'/%3E%3Cpath d='M12 1.8v2.6M12 19.6v2.6M22.2 12h-2.6M4.4 12H1.8M19.1 4.9l-1.8 1.8M6.7 17.3l-1.8 1.8M19.1 19.1l-1.8-1.8M6.7 6.7L4.9 4.9'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='4.6'/%3E%3Cpath d='M12 1.8v2.6M12 19.6v2.6M22.2 12h-2.6M4.4 12H1.8M19.1 4.9l-1.8 1.8M6.7 17.3l-1.8 1.8M19.1 19.1l-1.8-1.8M6.7 6.7L4.9 4.9'/%3E%3C/svg%3E") center / contain no-repeat;
}
.theme-toggle::after {
  right: 13px;
  background: var(--text-dim);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'/%3E%3C/svg%3E") center / contain no-repeat;
}
:root[data-theme="dark"] .theme-toggle::before { background: var(--text-dim); }
:root[data-theme="dark"] .theme-toggle::after { background: var(--text); }
.theme-toggle:hover::before { background: #c07c1e; }
:root[data-theme="dark"] .theme-toggle:hover::before { background: var(--text-dim); }
:root[data-theme="dark"] .theme-toggle:hover::after { background: #c07c1e; }

/* ── the bar below the desktop widths: ONE row at every size, after the
   reference capture (2026-09-18) — the name at the left, the section
   pair riding the middle, the info links and the theme pill at the
   right. No burger: everything stays visible, only smaller. */
@media (max-width: 1068px) {   /* below the LARGE tier the bar centres its pair */
  /* doubled selectors: several pages restate the bar locally (inline
     styles win source order), so these must outrank one class */
  .nav .nav-center {
    position: static;
    display: flex;
    align-items: center;
    gap: 24px;  /* +8px, per user 2026-09-25 */
    margin-inline: auto;
    padding: 0 14px;
  }
  /* static flow would put "UX UI" after "Architecture"; keep the pair's
     desktop order */
  .nav .nav-center .ux { position: static; order: -1; }
  .nav .nav-right { margin-left: 0; }
}

/* nav.js used to fold the bar behind a burger; the injected button now
   stays hidden at every width */
.nav-burger { display: none !important; }

@media (max-width: 734px) {   /* SMALL tier (Apple: ≤734) — the iPhone layout */
  /* phones use overlay scrollbars, so the reserved gutter only unbalanced
     the side padding — right gutter off, margins equal again */
  html { scrollbar-gutter: auto; }
  /* the bar keeps the page's own margin, so the logo and About stand on
     the same lines as the gallery prints and the footer below */
  .nav.nav { padding: 12px var(--page-pad) 10px; }
  /* tripled/link-level selectors: the case studies restate the bar's
     type locally on the links themselves, and must lose here */
  .nav.nav .nav-logo { font-size: 19px; }
  .nav.nav .nav-center { gap: 24px; padding: 0 4px; }  /* +8px, per user 2026-09-25 */
  .nav.nav .nav-right { gap: 6px; align-items: center; }
  /* legibility floor: 10px read as decoration, not labels. 13px fits the
     row comfortably now that About stands alone on the right */
  .nav.nav .nav-center a, .nav.nav .nav-right a { font-size: 13px; letter-spacing: 0; }
  /* the row can't hold 13px labels on the very narrowest phones */
  @media (max-width: 350px) {
    .nav.nav .nav-center a, .nav.nav .nav-right a { font-size: 12px; }
    .nav.nav .nav-center { gap: 18px; }  /* +8px, per user 2026-09-25 */
  }

  /* the theme pill at two-thirds, same anatomy — selectors are bare so the
     sizing follows the pill when nav.js moves it into the footer on phones */
  .theme-toggle { width: 48px; height: 22px; border-radius: 11px; top: -1px; }
  .theme-toggle i { top: 3px; left: 3px; width: 21px; height: 16px; border-radius: 8px; }
  :root[data-theme="dark"] .theme-toggle i { transform: translateX(21px); }
  .theme-toggle::before, .theme-toggle::after { width: 11px; height: 11px; margin-top: -5.5px; }
  .theme-toggle::before { left: 8px; }
  .theme-toggle::after { right: 8px; }

  /* phones: About alone holds the right corner — it carries the CV and
     contact details, so their own links step out of the bar */
  .nav-right a[href="cv.html"],
  .nav-right a[href^="mailto:"] { display: none; }

  /* phones: the pill rides the bottom bar (moved there by nav.js), and the
     bottom bar's type steps down a size (body-qualified: several pages
     restate the footer's 13px locally, and must lose here) */
  body footer { font-size: 11px; }
  footer .footer-links { gap: 14px; }
  footer .theme-toggle { top: 0; }
}

/* ── Soft image arrival ──
   tile images fade up over the card colour once decoded, instead of
   popping in abruptly as they load. the phone tile's hover photo layer
   manages its own opacity and is excluded. */
.card img:not(.photo-layer) { opacity: 0; transition: opacity .5s ease; }
.card img.loaded { opacity: 1; }
/* the courtyard hero loads eagerly and starts its fade first, so it takes
   a longer breath to land in step with its lazy neighbours */
.card.ar-hero img { transition-duration: .9s; }
@media (prefers-reduced-motion: reduce) {
  .card img:not(.photo-layer) { opacity: 1; transition: none; }
}

/* ── Tile captions ──
   a small white pill in the tile's lower-left naming the piece; appears
   on hover or keyboard focus, and stays visible on touch screens where
   there is no hover to invite */
.card .cap {
  position: absolute;
  left: 12px;
  bottom: 12px;
  z-index: 5;
  max-width: calc(100% - 24px);
  padding: 7px 12px;
  background: #fff;
  border-radius: 8px;
  font-size: 13px;
  letter-spacing: .3px;
  /* the pill stays white in both themes, so its ink stays fixed */
  color: #5e5454;
  box-shadow: 0 2px 10px rgba(0,0,0,.08);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .35s ease, transform .35s ease;
  pointer-events: none;
}
.card:hover .cap,
.card:focus .cap,
.card:focus-within .cap { opacity: 1; transform: none; }
@media (hover: none) {
  .card .cap { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .card .cap { transition: none; }
}

/* ── Phone device frame ──
   a quiet black bezel shared by the UX UI showcase and the case study's
   prototype stills: percentage paddings and elliptical corner radii keep
   the bezel's proportions at any rendered size. the frame stays black in
   both themes, like a physical object photographed on the page. */
.phone-device {
  position: relative;
  display: block;
  padding: 3%;
  background: #17171b;
  border-radius: 12% / 5.6%;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.16);
}
.phone-device .screen {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 9.5% / 4.6%;
  background: #eaedea;
  aspect-ratio: 402 / 874;
}
.phone-device .screen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.phone-device .island {
  position: absolute;
  top: 1.6%;
  left: 50%;
  translate: -50% 0;
  width: 27%;
  height: 2.4%;
  border-radius: 999px;
  background: #17171b;
  z-index: 2;
}

footer {
  /* a touch slimmer than the nav bar, so the page's top and bottom bands
     read level (2026-09-17, per user) */
  padding: 24px 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* the home page set the footer in the grotesk locally; shared here so
     every page's bottom bar matches it */
  font-family: var(--grtsk);
  font-size: 13px;
  color: var(--text-dim);
  border-top: 1px solid var(--line);
  margin: 0 var(--page-pad);
}
/* Contact sits inline with the footer text rather than floating over it */
.footer-links { display: flex; align-items: center; gap: 28px; }
.footer-links a { transition: color .25s ease; }
.footer-links a:hover { color: #d98d1f; }

/* only clickable text warms to the cursor's marigold */
a { transition: color .2s ease; }
a:hover { color: #d98d1f; }

/* ── Touch targets ──
   on coarse pointers every tap target reaches ~44px: the text links keep
   their drawn size but carry an invisible padded hit area (the negative
   margins hand the space back to the layout), and the theme pill grows a
   transparent border that widens its hit box without changing its look
   (absolutely-positioned children measure from the padding box, so the
   slider and glyphs stay put). */
@media (pointer: coarse) {
  .nav-center a, .nav-right a, .footer-links a {
    padding-block: 14px;
    margin-block: -14px;
    padding-inline: 3px;
    margin-inline: -3px;
  }
  .theme-toggle {
    box-sizing: content-box;
    border: 9px solid transparent;
    background-clip: padding-box;
    margin: -9px;
    /* the radius must carry the transparent ring too, or the visible
       pill's rounding is consumed by the border and it renders square:
       22 = 13 + 9 here, and 11 + 11 in the phone override below */
    border-radius: 22px;
  }
  /* dark mode recolours the pill with a `background` SHORTHAND, which
     silently resets background-clip to border-box and paints the tap
     ring — restate the clip at the same specificity */
  :root[data-theme="dark"] .theme-toggle { background-clip: padding-box; }
}
@media (pointer: coarse) and (max-width: 734px) {
  .theme-toggle { border-width: 11px; margin: -11px; }
}
/* iPads (touch, up to the 12.9" Pro's 1366px landscape): the bar is laid
   out like the phone's — About alone on the right (it carries the CV and
   contact details) and the theme pill in the footer (moved by nav.js with
   the same query). The tablet keeps its own, larger type. */
/* ── MEDIUM tier (Apple's 735–1068px): the layout stops squeezing — the
   content locks to a centred 692px column (Apple's medium content width)
   and the margins grow around it, so nothing reflows or collides as a
   window is dragged narrower; it only changes at the tier edges. --pad-m is
   the exact margin, measured by nav.js from the page width (browsers
   disagree on whether 100vw counts the scrollbar). The bar takes the
   phone/iPad arrangement. :root:root outranks the pages' own :root. ── */
@media (min-width: 735px) and (max-width: 1068px) {
  /* never below the phone's 24px floor — a stale 0 from nav.js must not
     strip the margins (iPad, 2026-09-25) */
  :root:root { --page-pad: max(24px, var(--pad-m, calc((100vw - 692px) / 2))); }
  .nav-right a[href="cv.html"],
  .nav-right a[href^="mailto:"] { display: none; }
  footer .theme-toggle { top: 0; }
}
@media (pointer: coarse) and (min-width: 701px) and (max-width: 1366px) {
  .nav-right a[href="cv.html"],
  .nav-right a[href^="mailto:"] { display: none; }
  footer .theme-toggle { top: 0; }
}
/* touch screens: a tap (and the focus handed back when the enlarged view
   closes) must not paint the keyboard-focus ring on the gallery tiles —
   keyboard users on fine-pointer machines keep it. !important because the
   pages restate the ring in their own style blocks, which load later. */
@media (pointer: coarse) {
  .card:focus-visible,
  .card:focus-visible .art,
  a.card:focus-visible .art { outline: none !important; }
  /* the enlarger focuses its ✕ (and arrows) on open — no ring on tap */
  .glb-close:focus-visible,
  .pz-close:focus-visible,
  .pz-nav:focus-visible { outline: none !important; }
}

/* ── Gallery clips on iOS ──
   WebKit paints a play glyph over a clip it refused to autoplay (Low Power
   Mode, or Auto-Play Video Previews off). The clips sit over their own
   stills, so a refused clip should rest as the still, not a button —
   touch-video.js keeps retrying it on the next gesture. */
.card-video::-webkit-media-controls-start-playback-button,
.about-art video::-webkit-media-controls-start-playback-button { display: none !important; -webkit-appearance: none; }
.card-video::-webkit-media-controls,
.about-art video::-webkit-media-controls { display: none !important; }
video::-webkit-media-controls-overlay-play-button,
video::-webkit-media-controls-play-button,
video::-webkit-media-controls-panel { display: none !important; -webkit-appearance: none; }
/* touch screens: a clip is invisible until it is really playing (its still
   shows through), so a refused clip can never surface the glyph — set and
   cleared by touch-video.js. !important: the pages' own hover rules and
   `.ambient-video .card-video { opacity: 1 }` must not win. */
video.ag-blocked { opacity: 0 !important; }
/* touch screens: a clip's first frame IS its still, so the hover fade
   only delayed the motion by 0.6s — it shows the moment it plays */
@media (hover: none) {
  .card-video, .about-art video { transition: none !important; }
}
