/* =============================================================================
   ink.css — hand-drawn linework, after the pen-and-ink maps it's modelled on.
   Loaded last on index.html, resume.html and contact.html, so it can replace
   the plain lines the other stylesheets draw. Delete this one <link> from a
   page and that page goes back to plain, even lines.

   The lines are small SVG strokes in img/strokes/, drawn by
   tools/make-strokes.py: their width swells and thins like a pen's, and
   underlines taper at the ends. They're made in the colours from site.css;
   if you change a colour there, run that script again.
   The script also builds them into css/strokes.css (loaded just before this
   file), as settings named after each file: var(--stroke-rule-rule) is
   img/strokes/rule-rule.svg. That way they arrive with the styles, rather
   than popping in a moment after the page draws. To use a different
   stroke here, use its var(--stroke-...) name and run the script again.

   How each kind is drawn:
     1. Section breaks — a long stroke tiled across the element's width, in
        place of its border (the border stays, transparent, to keep the
        spacing exactly as it was). The main breaks get a bolder stroke in
        --rule-ink; minor dividers keep a fine, pale one.
     2. Underlines — a short stroke stretched under each link (each line of a
        link that wraps gets its own). Neighbouring links get different
        strokes, and hovering swaps in the darker colour, as before.
     3. Picture frames — four strokes, one per side, drawn on a layer over the
        picture's edge.
     4. Pills — the tag filters and the resume's jump-bar buttons, outlined
        in two hand-drawn halves.

   Left plain on purpose: keyboard focus rings, which read best crisp. (The
   clip described in section 3 would cut a focus ring off, so it's lifted
   while a clipped link or button has keyboard focus.) Printing and the
   "more contrast" setting also keep the plain lines, as does Windows'
   high-contrast mode, which would strip these image-drawn lines (everything
   below only applies on screens, with no extra contrast or forced colours).
   ============================================================================= */

@media screen and (prefers-contrast: no-preference) and (forced-colors: none) {

/* -----------------------------------------------------------------------------
   1. SECTION BREAKS
   Each element below had a 1px rule on one side. Its border keeps its width
   (so nothing moves) but goes transparent, and the stroke is drawn as a
   background along that edge. --ink-x shifts where the tiled stroke starts,
   so stacked rules don't wobble in step.
   ----------------------------------------------------------------------------- */
:root {
  --rule-line:      var(--stroke-rule-rule);
  --section-line:   var(--stroke-section-rule-ink);   /* bolder, darker */
  --rule-soft-line: var(--stroke-rule-rule-soft);     /* paler */
  /* dashed, like a map's county line, for the minor dividers inside a
     section; the solid lines are the section breaks and bar edges */
  --dash-line:      var(--stroke-dash-rule);
}

/* rules along the BOTTOM edge */
.site, .trio, .hero, .kit h3, .jump {
  border-bottom-color: transparent;
  background-image: var(--rule-line);
  background-repeat: repeat-x;
  background-size: 400px 3px;
  background-position: var(--ink-x, 0px) bottom;
  background-origin: border-box;
}
.kit h3 { background-image: var(--rule-line); }      /* toolkit headings: solid */

/* rules along the TOP edge */
.foot, .styleinfo, .rsec, .ent, .line, .csec {
  border-top-color: transparent;
  background-image: var(--rule-line);
  background-repeat: repeat-x;
  background-size: 400px 3px;
  background-position: var(--ink-x, 0px) top;
  background-origin: border-box;
}
.ent, .line, .styleinfo { background-image: var(--dash-line); }

/* the main section breaks, and the line under the header: bolder and
   darker, so they read as drawn */
.site, .trio, .hero, .foot, .rsec, .csec {
  background-image: var(--section-line);
  background-size: 400px 4px;
}
/* The sticky bars (the header, and the resume's jump bar under it) have a
   background, to hide the page scrolling beneath them. It's cut away below
   their drawn line, so the page shows right up to the line rather than
   stopping at a straight edge under it: the mask is the whole bar minus the
   sliver beneath the stroke (drawn from the same shape, in the same place).
   That would also cut off the stuck header's shadow, so it has none: the
   drawn line does the separating. */
.site, .jump {
  -webkit-mask-image: linear-gradient(#000 0 0), var(--bar-outside);
          mask-image: linear-gradient(#000 0 0), var(--bar-outside);
  -webkit-mask-repeat: no-repeat, repeat-x;      mask-repeat: no-repeat, repeat-x;
  -webkit-mask-size: 100% 100%, var(--bar-line); mask-size: 100% 100%, var(--bar-line);
  -webkit-mask-position: 0 0, var(--ink-x, 0px) bottom;
          mask-position: 0 0, var(--ink-x, 0px) bottom;
  -webkit-mask-composite: source-out, source-over;
          mask-composite: subtract, add;
}
.site { --bar-outside: var(--stroke-section-outside-bottom); --bar-line: 400px 4px; }
.jump { --bar-outside: var(--stroke-rule-outside-bottom);    --bar-line: 400px 3px; }
.site, .site.stuck { box-shadow: none; }
/* On the resume the jump bar sits right under the header, so the strip cut
   away under the header's line would show the page scrolling between the
   two. The jump bar tucks 4px up beneath the header instead (the header is
   drawn on top), and fills that strip with its own background. Its buttons
   stay where they were: 4px more padding on top makes up for it. */
.jump {
  top: calc(env(safe-area-inset-top, 0px) + var(--headH) - 4px);
  margin-top: -4px;
  padding-top: 18px;
}
/* --jumpH (measured by js/resume.js) now includes those 4px, which already
   sit under the header, so sections stop in the same place as before */
.rsec { scroll-margin-top: calc(var(--headH) + var(--jumpH) + 4px); }

/* ...except where those had no rule */
.rsec:first-of-type, .ent:first-child, .line:first-child, .csec:first-of-type { background-image: none; }
/* resume on phones: the section names' coral underline separates the
   sections, so no drawn break line above them (css/resume.css) */
@media (max-width: 780px) { .rsec { background-image: none; } }

/* stagger the strokes */
:is(.rsec, .ent, .line, .kit h3, .csec):nth-child(3n+2) { --ink-x: -137px; }
:is(.rsec, .ent, .line, .kit h3, .csec):nth-child(3n)   { --ink-x: -271px; }
.hero { --ink-x: -83px; }
.foot { --ink-x: -199px; }


/* -----------------------------------------------------------------------------
   2. UNDERLINES
   --ul is the stroke at rest, --ul-hover the darker one on hover; --ul-h is
   how tall the stroke is drawn (about twice the old border's width, since
   the stroke only fills about half its height).
   ----------------------------------------------------------------------------- */
.trio a, .btn, .plinks a, .storylinks a, .pshots a.pcap, .scard-ttl > span,
.desc a, .award a, .rsec a, .elsewhere a, .freelance a {
  --ul:       var(--stroke-underline-accent-soft-1);
  --ul-hover: var(--stroke-underline-accent-1);
  --ul-h: 4px;
}
.trio a { --ul-h: 6px; }                          /* the intro's thicker underline */

/* neighbouring links get different strokes */
:is(.trio a, .plinks a, .storylinks a, .desc a, .award a, .rsec a, .elsewhere a, .freelance a):nth-of-type(3n+2),
:is(.storylinks, .plinks, .storycards, .elsewhere ul) > :nth-child(3n+2) :is(a, .scard-ttl > span) {
  --ul:       var(--stroke-underline-accent-soft-2);
  --ul-hover: var(--stroke-underline-accent-2);
}
:is(.trio a, .plinks a, .storylinks a, .desc a, .award a, .rsec a, .elsewhere a, .freelance a):nth-of-type(3n),
:is(.storylinks, .plinks, .storycards, .elsewhere ul) > :nth-child(3n) :is(a, .scard-ttl > span) {
  --ul:       var(--stroke-underline-accent-soft-3);
  --ul-hover: var(--stroke-underline-accent-3);
}

/* draw them */
.trio a, .btn, .plinks a, .storylinks a, .pshots a.pcap, .scard-ttl > span,
.desc a, .award a, .rsec a, .elsewhere a, .freelance a,
.site nav a, .totop {
  border-bottom-color: transparent;
  background-image: var(--ul, none);
  background-repeat: no-repeat;
  background-size: 100% var(--ul-h, 4px);
  background-position: left bottom;
  background-origin: border-box;
  -webkit-box-decoration-break: clone;            /* a link that wraps gets a stroke per line */
          box-decoration-break: clone;
}
.trio a:hover, .btn:hover, .plinks a:hover, .storylinks a:hover, .pshots a.pcap:hover,
.storycards a:hover .scard-ttl > span, .desc a:hover, .award a:hover, .rsec a:hover,
.elsewhere a:hover, .freelance a:hover {
  border-bottom-color: transparent;
  background-image: var(--ul-hover);
}

/* the header nav: the current page in coral; others get a pale stroke on hover */
.site nav a:hover { border-bottom-color: transparent; --ul: var(--stroke-underline-rule-2); }
.site nav a[aria-current] { border-bottom-color: transparent; --ul: var(--stroke-underline-accent-3); }

/* Back to top: a thin coral stroke, ink on hover */
.totop { --ul: var(--stroke-underline-accent-1); --ul-h: 2.5px; }
.totop:hover { border-bottom-color: transparent; --ul: var(--stroke-underline-ink-1); }


/* -----------------------------------------------------------------------------
   3. PICTURE FRAMES
   The frame's own border goes transparent, and a layer on top of the
   picture (::after) draws four strokes along its edges. The layer ignores
   the mouse, so clicks still reach the picture.
   The picture is then clipped to the strokes' outer edge, so it never shows
   outside its hand-drawn frame where the line thins or wanders in: the mask
   starts with the whole picture and cuts away the thin sliver outside each
   stroke (img/strokes/frame-outside-*.svg, drawn from the same shapes). The
   --frame-* values are shared by the strokes and the clip, so the two always
   line up.
   ----------------------------------------------------------------------------- */
:root {
  --frame-strokes:
    var(--stroke-frame-h-stroke), var(--stroke-frame-h-stroke),
    var(--stroke-frame-v-stroke), var(--stroke-frame-v-stroke);
  --frame-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
  --frame-size: 400px 3px, 400px 3px, 3px 400px, 3px 400px;
  /* top, bottom, left, right — each starting at a different point */
  --frame-position: 0 0, -173px 100%, 0 -91px, 100% -229px;

  /* the clip: the whole picture (the first layer) minus the four slivers
     outside the strokes (the rest, added together first) */
  --clip-image: linear-gradient(#000 0 0),
    var(--stroke-frame-outside-top), var(--stroke-frame-outside-bottom),
    var(--stroke-frame-outside-left), var(--stroke-frame-outside-right);
  --clip-repeat: no-repeat, var(--frame-repeat);
  --clip-size: 100% 100%, var(--frame-size);
  --clip-position: 0 0, var(--frame-position);
}
.gframe, .scard-img, .pshots:not(.scatter) a:not(.pcap), .pshots:not(.scatter) .pstill,
.pvid, .panel-body, .cartouche {
  border-color: transparent;
  background-clip: padding-box;                   /* no sliver of background outside the stroke */
}
.scard-img, .pvid { position: relative; }

.gframe::after, .scard-img::after, .pshots:not(.scatter) a:not(.pcap)::after,
.pshots:not(.scatter) .pstill::after, .pvid::after, .panel-body::after, .cartouche::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 3;                                     /* above the "In the book" badge */
  pointer-events: none;
  background-image: var(--frame-strokes);
  background-repeat: var(--frame-repeat);
  background-size: var(--frame-size);
  background-position: var(--frame-position);
}

/* clip each picture to its frame. The tiles keep their drop shadow, which a
   clip would cut off, so on a tile it's the picture inside (and the tile's
   pale background moves onto the picture) that gets clipped. */
.gframe > :is(img, video),
.scard-img, .pshots:not(.scatter) a:not(.pcap), .pshots:not(.scatter) .pstill,
.pvid, .panel-body {
  -webkit-mask-image: var(--clip-image);         mask-image: var(--clip-image);
  -webkit-mask-repeat: var(--clip-repeat);       mask-repeat: var(--clip-repeat);
  -webkit-mask-size: var(--clip-size);           mask-size: var(--clip-size);
  -webkit-mask-position: var(--clip-position);   mask-position: var(--clip-position);
  /* the whole picture, minus the slivers (which are added together below it) */
  -webkit-mask-composite: source-out, source-over, source-over, source-over, source-over;
          mask-composite: subtract, add, add, add, add;
  /* measured from inside the (transparent) border, like the strokes */
  -webkit-mask-origin: padding-box;               mask-origin: padding-box;
}

/* the "In the book" badge sits in a tile's top-left corner: the frame is
   drawn over it, and it's clipped at the top and left lines like the picture */
.gframe > .marker {
  -webkit-mask-image: linear-gradient(#000 0 0),
    var(--stroke-frame-outside-top), var(--stroke-frame-outside-left);
          mask-image: linear-gradient(#000 0 0),
    var(--stroke-frame-outside-top), var(--stroke-frame-outside-left);
  -webkit-mask-repeat: no-repeat, repeat-x, repeat-y;        mask-repeat: no-repeat, repeat-x, repeat-y;
  -webkit-mask-size: 100% 100%, 400px 3px, 3px 400px;        mask-size: 100% 100%, 400px 3px, 3px 400px;
  /* the same starting points as the frame's top and left strokes */
  -webkit-mask-position: 0 0, 0 0, 0 -91px;                 mask-position: 0 0, 0 0, 0 -91px;
  -webkit-mask-composite: source-out, source-over, source-over;
          mask-composite: subtract, add, add;
}
.gframe { background-color: transparent; }
.gframe > :is(img, video) { background-color: var(--plate); }

/* Shadows. drop-shadow follows the picture's actual (clipped) outline, so
   it hugs the drawn line; a box-shadow follows the old straight edge, which
   leaves a hairline pale gap just outside the line.
   But drop-shadow is costly: it's recalculated whenever what's under it
   changes, so on the tiles that are looping videos it reruns every frame,
   and with a dozen of them the page stutters as it scrolls. So the tiles
   keep their usual faint box-shadow at rest (the gap barely shows at that
   depth), and only the one tile under the pointer gets the drop-shadow lift.
   The book cover, a single still picture, keeps its drop-shadow. Same depth
   as --shadow and --shadow-lift in site.css. */
:root {
  --ink-shadow-lift: drop-shadow(0 3px 6px rgba(34,32,28,.10)) drop-shadow(0 18px 40px rgba(34,32,28,.14));
}
.gframe {
  transition: transform .22s cubic-bezier(.2,.7,.3,1), box-shadow .22s, filter .22s;
}
@media (hover: hover) {
  .gcard:hover .gframe { box-shadow: none; filter: var(--ink-shadow-lift); }
}
.hero-cover img { box-shadow: none; filter: var(--ink-shadow-lift); }

/* The open tile looks just like the others: same frame, same shadow (the
   coral "+" chip shows which one is open) */
.gcard[aria-expanded="true"] .gframe {
  border-color: transparent;
  box-shadow: var(--shadow);
}

/* The tiles' "+" chip (it opens the detail view), the panel's Close
   button, and the contact form's fields and Send button: the same four strokes, drawn as the button's own background, and
   the button clipped at their outer edge so its fill doesn't show past the
   line. Here the strokes start at the button's outer edge (border-box), so
   the clip does too. */
.chip, .gcard[aria-expanded="true"] .chip, .panel .close,
.cform input:not(.trap), .cform textarea, .send, .send:hover {
  border-color: transparent;
  background-image: var(--frame-strokes);
  background-repeat: var(--frame-repeat);
  background-size: var(--frame-size);
  background-position: var(--frame-position);
  background-origin: border-box;
  -webkit-mask-image: var(--clip-image);         mask-image: var(--clip-image);
  -webkit-mask-repeat: var(--clip-repeat);       mask-repeat: var(--clip-repeat);
  -webkit-mask-size: var(--clip-size);           mask-size: var(--clip-size);
  -webkit-mask-position: var(--clip-position);   mask-position: var(--clip-position);
  -webkit-mask-composite: source-out, source-over, source-over, source-over, source-over;
          mask-composite: subtract, add, add, add, add;
  -webkit-mask-origin: border-box;               mask-origin: border-box;
}

/* The clip would also cut off the focus ring drawn just outside a link or
   button, so it's lifted while one has focus (keyboard focus; a form field
   shows its ring whenever you're typing in it) */
:is(.pshots:not(.scatter) a:not(.pcap), .panel .close, .send, .cform input, .cform textarea):focus-visible {
  -webkit-mask: none; mask: none;
}

/* the book cover is an <img>, which can't hold a layer: its frame is drawn in
   a 2px band of padding around the picture instead */
.hero-cover img {
  border: 0; padding: 2px;
  background-image: var(--frame-strokes);
  background-repeat: var(--frame-repeat);
  background-size: var(--frame-size);
  background-position: var(--frame-position);
}

/* -----------------------------------------------------------------------------
   4. PILLS — the tag filters (.pill) and the resume's jump bar (.jump a).
   Each outline is two images, the left and right halves, each a rounded end
   with a long straight run. Each is shown over half the button and scaled to
   its height, so the ends stay round whatever the pill's size; the straight
   runs meet in the middle. --label-ink at rest (the pill text matches it, and
   is dark enough to read at its size), dark on hover, coral when on.
   ----------------------------------------------------------------------------- */
.pill, .jump a {
  border-color: transparent;
  background-image: var(--stroke-pill-left-label-ink), var(--stroke-pill-right-label-ink);
  background-repeat: no-repeat;
  background-size: 50% 100%;
  background-position: left center, right center;
  background-origin: border-box;
}
.pill:hover, .jump a:hover {
  border-color: transparent;
  background-image: var(--stroke-pill-left-stroke), var(--stroke-pill-right-stroke);
}
.pill[aria-pressed="true"], .jump a.on {
  border-color: transparent;
  background-image: var(--stroke-pill-left-accent-ink), var(--stroke-pill-right-accent-ink);
}

}  /* end @media */
