/* ============================================================
   Seiji Ishiguro — "concept"
   Built to the artboards: black text, small, one block of type
   sitting ~12.8% from the left and ~17.8% down. Everything else
   is white space.

   Performance rules for this theme:
     - no web fonts, no images in CSS, no shadows, no filters
     - the only animated properties are transform and opacity,
       both compositor-only, so nothing here can cause layout
   ============================================================ */

:root {
  --ink:        #111111;
  --paper:      #ffffff;
  --highlight:  #fbf1a9;   /* pale - the travelling hover marker */
  --pocket:     #fcf6c8;   /* a shade lighter, so the marker still reads on it */
  --olive:      #8c7c33;   /* sub-item text inside the pocket */
  --olive-faint:#c2b47e;   /* an opened section name - a label, not a target */
  --olive-dark: #5d5115;   /* the caption heading in the viewer */
  --panel:      #fbf3bd;   /* the viewer's caption panel */
  --muted:      #6a6a6a;

  /* set to e.g. 3px to soften the marker's edges */
  --hl-blur: 0px;

  /* The curve the dropdown items ride in on. Override with -Ease on build.ps1.
       slingshot  1.56  loose, obvious bounce
       gentle     1.26  a hint of overshoot            <- current
       snap       1.35  quick, tight bounce
       expo       none  fast then glides, no bounce
       soft       none  plain ease-out, calmest        */
  --ease-drop: cubic-bezier(.34, 1.26, .64, 1);
  --drop-ms: 280ms;

  /* Cursor resting diameter. The extended lengths and the easing live in
     highlight.js (CD / HINT / EXT), because both the pill and the plus are
     driven continuously per frame rather than by CSS states. */
  --cd: 9px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue",
          Helvetica, Arial, sans-serif;

  --t-name: 19px;
  --t-nav:  14px;
  --t-meta: 12px;

  /* the concept's position, held as a grid the layout can rely on */
  --col:  12.8vw;   /* left margin */
  --top:  17.8vh;   /* first baseline block */
  --lead: 29px;     /* nav line rhythm */

  --nav-w:  200px;  /* the column reserved for the nav in main's margin */
  --gutter: 120px;  /* clear air between the nav and the work */

  /* The drawer, desktop only. --nav-col holds the labels ("Architecture" is the
     widest at 75px, plus its 9px bleed). --drawer-gap is the air on BOTH sides
     of the drawer - the same measure between the labels and the drawer as
     between the drawer and the work, which is what stops it crowding the
     images. The drawer takes what is left - it runs from 95px to 300px of the
     320px, so 205px, against the 180px of "Bibliotheque Nationale Study", the
     longest name there is. */
  --nav-col:     84px;
  --drawer-gap:  20px;
  --slide-ms:   300ms;
  --ease-slide: cubic-bezier(.22,.68,.24,1);

  /* image viewer: a print on a clean desk, not a cinema */
  --lb-bg:         #ffffff;
  --lb-ink:        #111111;
  --lb-dim:        #6a6a6a;
  --lb-btn-bg:     var(--highlight);
  --lb-btn-ink:    #111111;
  --lb-img-shadow: none;
  --lb-img-max-w:  86vw;
  --lb-img-max-h:  84vh;
  --lb-panel-w:    15vw;
  --lb-chip-top:   11vh;   /* where the caption chip sits */
  --lb-chip-right:  3vw;

  color-scheme: light;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-nav);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--highlight); color: var(--ink); }

/* ---------- the type block ------------------------------- */

.nav {
  position: fixed;
  left: var(--col);
  top: var(--top);
  z-index: 20;
  isolation: isolate;
}

.nav__name {
  font-size: var(--t-name);
  font-weight: 400;
  letter-spacing: -0.005em;
  margin: 0 0 22px;
}

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

/* inline-block, not block: as a block the anchor spanned the whole column, so
   "Art" had a 180px hit box around a 19px word - 162px of it hanging off to the
   right. That misplaced both the hover target and the entry-direction maths. */
.nav__item {
  display: inline-block;
  font-size: var(--t-nav);
  line-height: var(--lead);
  white-space: nowrap;
}
.nav__list li { line-height: 0; }   /* stop inline-block baseline gaps */

/* a section plus its (collapsible) project list.

   Square corners, deliberately. The hover marker is a hard-edged rectangle, and
   an 8px radius here meant the yellow changed shape the moment you clicked -
   the marker's corners and the pocket's did not agree. One shape for both. */
.nav__group {
  padding: 0 9px;
  margin: 0 -9px;
  transition: background-color 200ms linear;
}
.nav__group.is-open { background: var(--pocket); }

/* clipped, so the items can start tucked up behind the section name and slide
   out from under it rather than appearing on top of it */
.nav__pocket { height: 0; overflow: hidden; }

/* The drawer's sliding yellow. Only exists on desktop - below the breakpoint
   the pocket is a pushdown and the group carries the colour itself. */
.nav__curtain { display: none; }

.nav__sub { list-style: none; margin: 0; padding: 0 0 8px; }
.nav__sub .nav__item {
  color: var(--olive);
  line-height: 25px;
  transform: translateY(-16px);
  opacity: 0;
}
.nav__sub .nav__item[aria-current="page"] { color: var(--ink); }

/* the drop-in. The spring lives HERE, not on the pocket height: overshooting
   the height opened a 15px band of bare yellow under the last item on the way
   out and back. Transform cannot do that, because it does not change the box. */
.nav__group.is-open .nav__sub .nav__item {
  transform: translateY(0);
  opacity: 1;
  transition:
    transform var(--drop-ms) var(--ease-drop) calc(var(--i, 0) * 24ms),
    opacity   150ms          linear           calc(var(--i, 0) * 24ms);
}

/* ---------- About ----------------------------------------- */
/*
   Same yellow as a section, deliberately. It used to open in --highlight, a
   visibly deeper shade than the --pocket the sections use, so About read as a
   different kind of thing when it is just another drawer.
*/
.nav__group--about.is-open { background: var(--pocket); }

.nav__about {
  padding: 14px 4px 22px 0;
  max-width: 46ch;
  opacity: 0;
  transform: translateY(-10px);
  /* the inline-block fix zeroes line-height on nav list items; that cascades
     in here and collapses the CV rows on top of each other */
  line-height: 1.5;
}
.nav__group--about.is-open .nav__about {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 200ms linear 90ms, transform 300ms cubic-bezier(.34,1.56,.64,1) 90ms;
}
.nav__about p { margin: 0 0 1em; color: var(--ink); font-size: var(--t-nav); line-height: 1.5; }
.nav__about a { border-bottom: 1px solid rgba(0,0,0,.22); }
.nav__about a:hover { border-color: var(--ink); }

.nav__cv {
  margin: 0 0 1.4em;
  display: grid;
  grid-template-columns: 5.5em 1fr;
  gap: 6px 14px;
  line-height: 1.5;
}
.nav__cv dt { color: var(--olive); font-size: var(--t-meta); padding-top: 2px; line-height: 1.5; }
.nav__cv dd { margin: 0; font-size: var(--t-nav); line-height: 1.5; }
.nav__contact { font-size: var(--t-meta); line-height: 1.6; }

/* ============================================================
   THE DRAWER  (desktop only)

   A file cabinet rather than an accordion: the labels stay put and
   the list slides out to their right, so opening a section never
   pushes the ones below it down.

   The open label sits in a yellow tab that runs into the drawer, so
   the two read as one object. Four arrangements were built and
   compared; this one was chosen on 2026-10-11 and the others were
   removed along with the picker that switched between them.

   Below the breakpoint none of this applies - there the nav is a
   full-width block above the content, a drawer has nowhere to go,
   and the pocket stays the pushdown it already was.
   ============================================================ */

@media (min-width: 901px) {
  /* The label column stops widening to fit its contents: it is now just wide
     enough for the longest section name, and the drawer starts after it. */
  .nav__list { width: var(--nav-col); }
  .nav__group { position: relative; }

  /*
     Out of the flow entirely - that is the whole point, since an absolutely
     positioned box cannot push its siblings anywhere.

     No z-index here, deliberately. A positioned box with z-index: auto does not
     start a stacking context, so the highlight layer (z-index 0, appended after
     the list) still paints above the drawer's yellow, while the project names
     inside it - z-index 1, via .hl-target - stay above the highlight. Give the
     pocket a z-index and the marker disappears behind the drawer.
  */
  /*
     Equal air either side. The drawer sits --drawer-gap clear of where the
     longest label ends and the same --drawer-gap clear of where the work
     begins, so it reads as a column in its own right rather than something
     crowding the page. `left` is measured from the group's padding box, which
     starts 9px left of the labels - hence the +9 built into both numbers.
  */
  .nav__pocket {
    position: absolute;
    top: 0;
    left: calc(var(--nav-col) + var(--drawer-gap));
    width: calc(var(--nav-w) + var(--gutter) + 9px
                - var(--nav-col) - var(--drawer-gap) * 2);
    height: auto;
    overflow: hidden;
    pointer-events: none;
  }
  .nav__group.is-open > .nav__pocket { pointer-events: auto; }

  /*
     The yellow is its own element, and it is the only thing that slides.

     It used to be the panel's own background, which meant a transform on the
     panel - and a transform makes a stacking context. The project names inside
     were then sealed into it, so their z-index: 1 could not rise above the
     highlight layer and the marker painted straight over the words. Split out,
     the names sit at z-index 1 in the nav's own context: above the layer, so the
     yellow goes behind the text where it belongs.
  */
  /*
     The 2px overhang on the left is not slack, it is the fix for a 1px pale
     sliver that ran down the drawer's left edge.

     .nav is at 12.8vw, so every offset inside it inherits a fraction: the
     drawer's left edge lands at 418.969 device pixels, not 419. The curtain is
     transformed, so it is a composited layer, and the pocket's overflow clip is
     applied in the compositor - where it snaps to whole pixels while the boxes
     either side of it antialias across the boundary. The result is a column
     that neither the yellow nor its neighbour fully covers.

     Starting the curtain outside its own clip means there is yellow on both
     sides of that boundary, so wherever the clip lands it lands on yellow. It
     still hides when closed: translateX(-100%) shifts it by its own width, so
     its right edge finishes 2px clear of the pocket.
  */
  .nav__curtain {
    display: block;
    position: absolute;
    inset: 0 0 0 -2px;
    background: var(--pocket);
    transform: translateX(-100%);
    transition: transform var(--slide-ms) var(--ease-slide);
  }
  .nav__group.is-open .nav__curtain { transform: translateX(0); }

  /* the panel carries no background and no transform - only the names move */
  .nav__sub {
    background: none;
    transform: none;
    padding: 2px 10px 10px;
  }
  .nav__about {
    background: none;
    transform: none;
    position: relative;      /* positioned, z-index 1: above the curtain */
    z-index: 1;
    padding: 10px 12px 16px;
    max-width: none;
    opacity: 0;
    transition: opacity 160ms linear;
  }
  .nav__group--about.is-open .nav__about {
    opacity: 1;
    transform: none;
    transition: opacity 200ms linear 90ms;
  }
  /* About drops rather than slides, so its curtain does too */
  .nav__group--about .nav__curtain { transform: translateY(-100%); }
  .nav__group--about.is-open .nav__curtain { transform: translateY(0); }

  /* the names ride out behind the drawer's leading edge, not down from above */
  .nav__sub .nav__item { transform: translateX(-14px); }
  .nav__group.is-open .nav__sub .nav__item { transform: translateX(0); }

  /* the marker layer has to reach over the drawer, or a project name gets no
     highlight - it is the nav's child, and the nav is only a label wide now.
     Sized to the whole column plus gutter, which is the most any drawer can
     take up. */
  .hl-layer { right: calc(-24px - var(--nav-w) - var(--gutter)); }

  /* --- the tab ------------------------------------------- */
  /*
     The open section's label sits in a tab that reaches all the way to the
     drawer, so the two read as one object rather than as a label and a panel.
     Chosen 2026-10-11 over a free-standing panel with white either side. The
     group's own box is only the label row, so its background paints the tab and
     nothing more - the drawer carries its own yellow.

     It has to be the margin that stretches it, not the padding. Everything here
     is border-box and the list has a fixed width, so the group's box is already
     decided - padding only pushes its contents inward and the tab stayed 2px
     short of the drawer. The negative margin is what widens the box: -9px is
     the bleed every group has, and this takes it out to the drawer's edge.

     The extra 2px runs the tab UNDER the drawer rather than up against it. Two
     boxes meeting exactly on a fractional device pixel is what left a pale
     sliver down the join; overlapped, the drawer's own yellow paints over the
     seam and there is no boundary to fall between.
  */
  .nav__group.is-open { background: var(--pocket); }
  .nav__group { margin-right: calc(7px - var(--drawer-gap)); }

  /* --- About ---------------------------------------------- */
  /*
     A body of text, not a six-row list, and it will not fit the drawer slot:
     measured at 212px wide it sets 807px tall against 604px of room on a
     900px window, so it scrolls and the CV rows break over three lines. It
     needs about 300px to read, and 300px starting where the section drawers
     start would run 75px into the work.

     So it opens directly beneath its own label instead, out of the flow like
     everything else here. That costs nothing: About is the last row, so there
     is nothing below it to push. It reaches from the labels' own left edge to
     --drawer-gap short of the work, the same stopping line the drawers use, and
     slides down out from behind its label rather than sideways.
  */
  .nav__group--about { position: relative; }
  .nav__group--about .nav__pocket {
    top: 100%;
    left: 0;
    width: calc(var(--nav-w) + var(--gutter) + 9px - var(--drawer-gap));
  }
  /* the stretched tab is a section idea - About's panel is already directly
     under its own label, with nothing off to the right to reach for */
  .nav__group--about { margin-right: -9px; }
}

/* everything the highlight can sit behind */
.hl-target { position: relative; z-index: 1; cursor: pointer; }

/*
   Once a section is open its name has nothing left to do - the things worth
   clicking are the projects underneath it. So it steps back: lighter, and
   dropped BEHIND the highlight layer instead of in front of it. The project
   names keep z-index 1 and stay in front, which is what marks them as the
   live targets.

   pointer-events: none does the rest - no pointerenter means no highlight
   animation on it at all, and no stray click. It is closed by clicking the
   name above, or by opening another section.
*/
.nav__group.is-open > .nav__head {
  color: var(--olive-faint);
  z-index: 0;
  pointer-events: none;
}
.nav__group.is-open > .nav__head[aria-current="page"] { color: var(--olive); }

/* ---------- the travelling highlight ---------------------- */
/*
   One 1x1px element inside a layer. Position and size are BOTH
   carried by a single transform - translate() places it, scale()
   sizes it - so nothing here can trigger layout or paint.

   The blur lives on the LAYER, not the marker: blurring a scaled
   element would stretch the blur with it (a 3px blur on a 1px box
   scaled 35x vertically becomes a 105px smear). Blurring the
   parent applies it in screen space, after the transform, so the
   softness stays even on every edge.

   There is deliberately no `filter` declared here. build.ps1 adds
   one only when -HlBlur is above zero, because filter: blur(0px)
   is not free: any filter promotes the subtree to its own layer
   and resamples it, which softened the very edges it was meant to
   leave alone.
*/
.hl-layer {
  position: absolute;
  inset: -24px;                       /* bleed room so blur is not clipped */
  pointer-events: none;
  z-index: 0;
}

/*
   The highlight IS the fill - there is no separate solid marker.
   A clip box sits on the text with a circle inside that expands
   from a point placed OUTSIDE the box, so the leading edge crosses
   the text as a shallow arc instead of ballooning out of the border.

   It needs real width/height (a circle inside a non-uniformly
   scaled 1x1 box would be squashed into an ellipse). The geometry is
   written on entry and never animated; only the circle's transform
   moves, so the animation itself stays on the compositor.
*/
/* two of these exist: the one filling, and the previous one fading out
   underneath it so the highlight lingers rather than snapping away */
.hl-bubble {
  position: absolute;
  left: 0; top: 0;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/* Size and position are written per entry by highlight.js: the circle is built
   at its full diameter and scaled 0 -> 1, never up from a 2px seed. A composited
   layer is rastered once, so a 2px circle blown up 40x arrived as 2px of curve
   smeared across 80 - a furry leading edge. At full size it is rastered at 1:1
   and only ever shrinks. */
.hl-circle {
  position: absolute;
  border-radius: 50%;
  background: var(--highlight);
  transform-origin: 50% 50%;
  will-change: transform;
}

/* ---------- dot cursor ------------------------------------ */

/*
   Dot, pill and plus are all the same two bars crossed at the centre:
   both square -> they overlap into a dot; grow the vertical one -> pill;
   grow both -> plus. Nothing is swapped out, so the states tween.

   The caps have to stay circular, which means animating width/height
   rather than transform - a scaled border-radius stretches into an
   ellipse. It is two ~10px elements inside a fixed-position wrapper with
   no layout dependents, so the relayout is as cheap as it gets. The
   wrapper still carries position and swell on transform alone.
*/
.cursor-dot {
  position: fixed;
  left: 0; top: 0;
  width: 0; height: 0;
  pointer-events: none;
  z-index: 500;
  will-change: transform;
  transition: opacity 140ms linear;

  /* Inversion. The bars are painted WHITE and differenced against whatever is
     underneath: |white - white| = black on the page, |white - black| = white
     over a dark photograph. No sampling, no JS, correct on every backdrop. */
  mix-blend-mode: difference;
}

/*
   Lengths come from JS (--v-len / --h-len), not from state classes, because
   the pill now tracks scroll speed and the plus tracks zoom depth - both
   continuous. JS eases toward its target each frame, so there is no CSS
   transition here to fight it.
*/
.cursor-dot span {
  position: absolute;
  left: 0; top: 0;
  background: #fff;              /* white, so difference inverts correctly */
  border-radius: 999px;
}

.cursor-dot__v {
  width: var(--cd, 9px);
  height: var(--v-len, var(--cd, 9px));
  margin-left: calc(var(--cd, 9px) / -2);
  margin-top:  calc(var(--v-len, var(--cd, 9px)) / -2);
}

.cursor-dot__h {
  width: var(--h-len, var(--cd, 9px));
  height: var(--cd, 9px);
  margin-left: calc(var(--h-len, var(--cd, 9px)) / -2);
  margin-top:  calc(var(--cd, 9px) / -2);
}

/*
   Hide the native pointer everywhere once the dot is running.

   This has to be universal and !important. Listing a few selectors was not
   enough: component rules like `.gal a[data-zoom] { cursor: zoom-in }` and
   `.lb.is-zoomed .lb__img { cursor: grab }` carry higher specificity, and
   lightbox.css loads after this file - so the native cursor kept reappearing
   over exactly the zoom surfaces. Those rules are left in place deliberately,
   because without JS the class is never added and they are still correct.
*/
html.has-dot-cursor,
html.has-dot-cursor *,
html.has-dot-cursor *::before,
html.has-dot-cursor *::after { cursor: none !important; }

/* ---------- content column -------------------------------- */

/* the old margin put the content at 376px while the nav ran to ~384px, so the
   work was starting before the menu had finished */
main {
  margin-left: calc(var(--col) + var(--nav-w) + var(--gutter));
  padding: var(--top) 6vw 12vh 0;
  min-height: 100vh;
}
.home main { padding: 0; }

h1 { font-size: var(--t-nav); font-weight: 400; margin: 0 0 4px; }
.meta { font-size: var(--t-meta); color: var(--muted); margin: 0 0 28px; }

.prose { max-width: 58ch; margin: 0 0 40px; }
.prose p { margin: 0 0 1em; }
.prose p:last-child { margin: 0; }

.blockhead {
  font-size: var(--t-nav); font-weight: 400; margin: 44px 0 14px;
  color: var(--muted);
}

.work { max-width: 58ch; margin: 44px 0 0; }
.work h3 { font-size: var(--t-nav); font-weight: 400; margin: 0 0 8px; }

/* ---------- galleries ------------------------------------- */

.gal { margin: 0 0 34px; }
.gal figure { margin: 0; }
.gal img { background: #f2f2f2; cursor: zoom-in; }
.gal figcaption { font-size: var(--t-meta); color: var(--muted); margin-top: 7px; }

.g-stack { display: block; max-width: 900px; }
.g-stack figure + figure { margin-top: 42px; }

.g-2, .g-3, .g-4 { display: grid; gap: 18px; align-items: start; }
.g-2 { grid-template-columns: repeat(2, 1fr); max-width: 900px; }
.g-3 { grid-template-columns: repeat(3, 1fr); max-width: 1100px; }
.g-4 { grid-template-columns: repeat(4, 1fr); }

.g-m3, .g-m5 { column-gap: 18px; }
.g-m3 { columns: 3; }
.g-m5 { columns: 5; }
.g-m3 figure, .g-m5 figure { break-inside: avoid; margin-bottom: 18px; }

/* ---------- slider ---------------------------------------- */
/*
   One image at a time, an arrow either side (Line Library's process sheets).
   The strip scrolls sideways and snaps, so the browser does the moving and it
   swipes on touch for free; slider.js only points the arrows.

   Each image is capped to the window's height. These sheets are portrait, and
   at the stack's 900px they would stand 1,400px tall - the arrows, centred on
   the strip, would be below the fold. Capped and centred in its frame, the
   picture keeps white either side for the arrows to sit in.

   The arrows and the strip share one grid cell rather than being positioned
   against the whole slider, which would centre them on strip + counter and
   leave them sitting half a line low.
*/
.slider { display: grid; max-width: 900px; margin: 0 0 34px; }
.slider > .g-slide { grid-area: 1 / 1; margin: 0; }

.g-slide {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.g-slide::-webkit-scrollbar { display: none; }
.g-slide figure {
  flex: 0 0 100%;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* --r is the image's width / height, written by build.ps1. Sizing the link from
   it gives the slide a definite box before the file arrives - shrink-wrapping an
   unloaded image measured 0 x 0. */
.g-slide a { display: block; width: min(100%, calc(82vh * var(--r, 1))); }
.g-slide img { width: 100%; }

.slider__btn,
.slider__count { display: none; }          /* inert until slider.js runs */
.slider.is-ready .slider__btn { display: block; }
.slider.is-ready .slider__count { display: block; }

.slider__btn {
  grid-area: 1 / 1;
  align-self: center;
  z-index: 1;
  appearance: none; border: 0; padding: 0;
  background: transparent;
  color: var(--ink);
  font: 34px/1 var(--font);
  width: 48px; height: 72px;
  cursor: pointer;
  transition: opacity 140ms linear;
}
.slider__btn--prev { justify-self: start; }
.slider__btn--next { justify-self: end; }
.slider__btn:hover,
.slider__btn:focus-visible { background: var(--highlight); outline: none; }
.slider__btn[aria-disabled="true"] { opacity: .18; background: transparent; }

.slider__count {
  grid-area: 2 / 1;
  margin: 7px 0 0;
  text-align: center;
  font-size: var(--t-meta);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---------- about ----------------------------------------- */

/*
   about.html carries the same text twice: once in the nav pocket, where the
   panel expands in place, and once here in the page body. With JS running that
   showed as the description a second time, underneath the yellow field.

   The body copy is the no-JS fallback and nothing else, so it is hidden the
   moment scripting is confirmed. `html.js` is set by an inline script in the
   head - before first paint, so the duplicate never flashes. Without JS the
   class is never added and about.html reads as an ordinary page.
*/
html.js .about { display: none; }

.about { max-width: 58ch; }
.about h2 { font-size: var(--t-nav); font-weight: 400; color: var(--muted); margin: 34px 0 8px; }
.cv { margin: 0; display: grid; grid-template-columns: 5.5em 1fr; gap: 3px 14px; }
.cv dt { color: var(--muted); font-size: var(--t-meta); padding-top: 2px; }
.cv dd { margin: 0; }

/* ---------- responsive ------------------------------------ */

/* ============================================================
   MOBILE
   The nav stops being a fixed side column and becomes a block at
   the top of the page. Everything hover-driven is switched off,
   and the tap targets are grown to something a thumb can hit.
   ============================================================ */

@media (max-width: 900px) {
  :root {
    --col: 7vw;
    --top: 9vh;
    --lead: 44px;          /* row height = tap target */
  }

  /* padding on BOTH sides now: the group's -9px bleed used to run off the
     right edge and gave the page a 10px horizontal scroll */
  .nav {
    position: static;
    padding: 9vh 7vw 0;
    isolation: auto;
  }
  .nav__name { font-size: 22px; margin-bottom: 14px; }

  /* full-width rows. There is no hover marker to mis-size on touch, so a
     large hit area is all upside - "Art" was a 19px-wide target. */
  .nav__item {
    display: block;
    line-height: var(--lead);
  }
  .nav__group { margin: 0; padding: 0 10px; }
  .nav__sub .nav__item { line-height: 40px; }
  .nav__sub { padding-bottom: 4px; }

  main { margin-left: 0; padding: 24px 7vw 14vh; }

  .g-3, .g-4 { grid-template-columns: repeat(2, 1fr); }
  .g-m5 { columns: 3; }

  .hl-layer, .cursor-dot { display: none; }   /* no hover on touch */
}

/* About opens as a full-width sheet rather than a side panel */
@media (max-width: 900px) {
  .nav__group--about .nav__pocket { width: auto; }
  .nav__group--about.is-open .nav__pocket { width: auto; transition: none; }
  .nav__about { max-width: none; padding: 10px 0 20px; }
  .nav__cv { grid-template-columns: 5em 1fr; }
}

@media (max-width: 560px) {
  .g-2, .g-3, .g-4 { grid-template-columns: 1fr; }
  .g-m3, .g-m5 { columns: 2; }
}

@media (prefers-reduced-motion: reduce) {
  .hl-circle, .hl-bubble, .cursor-dot { transition: none !important; }
}
