/* ============================================
   INTERESTS THEME - wallpaper, reading column, customisation panels
   Loaded after styles.css, so every rule here is free to override the
   Interests rules in that file. Nothing in this stylesheet may touch any
   other route: every selector is gated on BOTH `html.is-int-theme` (set by
   js/interests-theme.js while the route is /interests) and
   `body[data-route="/interests"]` (set by js/router.js). If the theme script
   never runs, the page keeps its original editorial layout instead of a
   column floating over a blank wallpaper - that is the whole point of the
   double gate.
   ============================================ */


/* ============================================
   CUSTOM PROPERTY CONTRACT
   js/interests-theme.js writes all of these on document.documentElement as
   inline styles, which outrank the :root defaults below. Every numeric value
   carries its CSS unit, because these land straight in filters, lengths and
   colours here - a bare number is not usable by this file.

     --int-wall-url      url("...") or none
     --int-wall-x/y      <percentage>  framing, 0%..100%, 50% is centred
     --int-wall-zoom     <percentage>  100%..160%
     --int-wall-blur     <length>      0px..30px
     --int-wall-gray     <percentage>  0%..100%
     --int-wall-darken   <percentage>  0%..100%
     --int-tint          <color>
     --int-tint-a        <percentage>  0%..100%
     --int-blend         a mix-blend-mode keyword
     --int-vignette      <percentage>  0%..100%
     --int-col-start     <length>      use vh, e.g. 82vh
     --int-col-w         <length>      420px..1100px
     --int-col-a         <percentage>  0%..100%
     --int-col-blur      <length>      0px..30px
     --int-col-edge      <length>      0px..200px
     --int-col-fill      <color>
     --int-col-darken    <percentage>  0%..100%
     --int-col-gray      <percentage>  0%..100%
     --int-ink           <color>
     --int-size          <length>      13px..28px
     --int-weight        <number>      100..900
     --int-line          <number>      1..2.4
     --int-track         <length>      use em, e.g. -0.02em
     --int-head-scale    <number>      1..2.5
     --int-head-weight   <number>      100..900
     --int-distress      <percentage>  0%..100%

   Two extras this file also reads, both optional:
     --int-col-texture   0 or 1, the Reading column "Paper texture" switch
     --int-font          a font stack. It must be REMOVED, not set to a
                         keyword, for "System default": every typeface rule
                         below is written as var(--int-font, <original stack>)
                         so that removing it restores the page's own faces.
     --int-font-head     a display stack for the headings (the name, the
                         section and book titles), set by a pairing face and
                         removed otherwise, when the headings follow
                         --int-font like everything else.

   --int-measure is derived here, not written by the script.

   The @property blocks register everything else so that a value arriving in
   the wrong form (a bare number, a px where a percentage belongs) is dropped
   at parse time and the cascade falls back to the :root default, instead of
   becoming invalid at computed-value time and blanking the declaration.
   ============================================ */

@property --int-wall-x      { syntax: "<percentage>"; inherits: true; initial-value: 50% }
@property --int-wall-y      { syntax: "<percentage>"; inherits: true; initial-value: 50% }
@property --int-wall-zoom   { syntax: "<percentage>"; inherits: true; initial-value: 100% }
@property --int-wall-blur   { syntax: "<length>";     inherits: true; initial-value: 0px }
@property --int-wall-gray   { syntax: "<percentage>"; inherits: true; initial-value: 0% }
@property --int-wall-darken { syntax: "<percentage>"; inherits: true; initial-value: 0% }
@property --int-tint        { syntax: "<color>";      inherits: true; initial-value: #160f04 }
@property --int-tint-a      { syntax: "<percentage>"; inherits: true; initial-value: 0% }
@property --int-vignette    { syntax: "<percentage>"; inherits: true; initial-value: 10% }
@property --int-col-start   { syntax: "<length>";     inherits: true; initial-value: 82vh }
@property --int-col-w       { syntax: "<length>";     inherits: true; initial-value: 660px }
@property --int-col-a       { syntax: "<percentage>"; inherits: true; initial-value: 72% }
@property --int-col-blur    { syntax: "<length>";     inherits: true; initial-value: 12px }
@property --int-col-edge    { syntax: "<length>";     inherits: true; initial-value: 0px }
@property --int-col-fill    { syntax: "<color>";      inherits: true; initial-value: #ffffff }
@property --int-col-darken  { syntax: "<percentage>"; inherits: true; initial-value: 0% }
@property --int-col-gray    { syntax: "<percentage>"; inherits: true; initial-value: 0% }
@property --int-ink         { syntax: "<color>";      inherits: true; initial-value: #2a2a2a }
@property --int-size        { syntax: "<length>";     inherits: true; initial-value: 16px }
@property --int-weight      { syntax: "<number>";     inherits: true; initial-value: 400 }
@property --int-line        { syntax: "<number>";     inherits: true; initial-value: 1.75 }
@property --int-head-scale  { syntax: "<number>";     inherits: true; initial-value: 1 }
@property --int-head-weight { syntax: "<number>";     inherits: true; initial-value: 600 }
@property --int-distress    { syntax: "<percentage>"; inherits: true; initial-value: 0% }

/* --int-track and --int-measure are deliberately unregistered: a registered
   length is computed where it is declared, which would resolve their `em` and
   `ch` against the root font rather than the reading column's own type. */
:root{
  /* The defaults: the same look as DEFAULTS in js/interests-theme.js. */
  --int-wall-url: none;
  --int-wall-x: 50%;
  --int-wall-y: 50%;
  --int-wall-zoom: 100%;
  --int-wall-blur: 0px;
  --int-wall-gray: 0%;
  --int-wall-darken: 13%;
  --int-tint: #160f04;
  --int-tint-a: 0%;
  --int-blend: multiply;
  --int-vignette: 10%;
  --int-col-start: 51vh;
  --int-col-w: 660px;
  --int-col-a: 100%;
  --int-col-blur: 12px;
  --int-col-edge: 0px;
  --int-col-fill: #16161a;
  --int-col-darken: 21%;
  --int-col-gray: 23%;
  --int-ink: #ececec;
  --int-size: 16px;
  --int-weight: 700;
  --int-line: 1.75;
  --int-track: -0.035em;
  --int-head-scale: 1.7;
  --int-head-weight: 600;
  --int-distress: 0%;

  /* The fill is free to be wider than comfortable reading; the text is not.
     The reference lets its column spill onto the painting below about 400px
     of band width, which loses half of every line against a cliff face. */
  --int-measure: clamp(38ch, calc(var(--int-col-w) - 120px), 74ch);

  /* Ink over the painting instead of over the fill. Cream rather than pure
     white, with two shadows: a tight one for edge definition on a bright sky
     and a soft one for mass against a dark foreground. */
  --int-ink-light: #f4f1ea;
  --int-shadow-light: 0 1px 3px rgb(0 0 0 / .6), 0 0 2px rgb(0 0 0 / .45);

  /* Panel chrome. These are fixed: the panel is UI, not content, so it must
     stay legible whatever the visitor does to the reading column's colours. */
  --int-ui-ink: #1f2937;
  --int-ui-ink-soft: #4b5563;
  --int-ui-line: rgb(15 23 42 / .12);
  --int-ui-glass: rgb(255 255 255 / .55);
  --int-ui-glass-solid: rgb(255 255 255 / .92);

  /* One procedural grain, tiled, used three times: the Paper texture switch,
     the Paper effect's CSS fallback and the distress mask. feTurbulence
     writes noise into the alpha channel as well as the colour channels, so
     the same file works as a mask and as a texture. */
  --int-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='.5'/%3E%3C/svg%3E");
  --int-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23d)'/%3E%3C/svg%3E");
}


/* ============================================
   STAGE AND UI VISIBILITY
   Both blocks are direct children of <body>, outside #view-interests, because
   js/router.js puts a 0.34s transform on the incoming view and a transform
   re-parents every position:fixed descendant for the length of the animation.
   They belong to this feature alone, so hiding them off-route is a safety net
   rather than a cross-route style.
   ============================================ */
#int-stage{ display: none }
body:not([data-route="/interests"]) #int-ui{ display: none !important }
.is-int-theme body[data-route="/interests"] #int-stage{ display: block }


/* ============================================
   WALLPAPER
   A fixed root at z-index -1. Research measured this on the owner's own page:
   html has no background, so body's white propagates to the canvas and a
   negative-z fixed layer paints above that canvas and below every piece of
   static content, with no stacking context added to <main>.
   ============================================ */
.is-int-theme body[data-route="/interests"] .int-wall{
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  contain: strict;
  /* First paint: the painting's darkest tone, under everything */
  background: var(--int-sb-track, #181a0c);   /* California Spring's darkest tone, the default painting */
}
/* First paint. While the painting downloads, its 10KB thumbnail, blurred,
   stands in for it: the head script sets both addresses before the body
   exists, so this is the very first frame rather than a white one. The
   painting itself is held back until it has decoded (html.is-wall-pending,
   removed by the same script) and then fades up over the thumbnail, instead
   of drawing itself in from the top in bands as the bytes arrive. The
   thumbnail stays underneath afterwards; the painting covers it. */
.is-int-theme body[data-route="/interests"] .int-wall::before{
  content: '';
  position: absolute;
  inset: -48px;
  background: var(--int-thumb-url) center / cover no-repeat;
  filter: blur(20px);
}
.is-int-theme.is-wall-pending body[data-route="/interests"] .int-wall-img[data-layer="a"]{
  opacity: 0;
}

/* Blur and grayscale ride together on one wrapper. The wrapper is inflated by
   2.5x the blur radius in every direction because a blurred edge samples
   nothing beyond its own box and would otherwise fade to the page colour a
   few pixels inside the viewport; .int-wall clips the overflow. */
.is-int-theme body[data-route="/interests"] .int-wall-filter{
  position: absolute;
  inset: calc(var(--int-wall-blur) * -2.5);
  filter: blur(var(--int-wall-blur)) grayscale(var(--int-wall-gray));
}

/* Two layers so a painting change can cross-fade. Framing works twice over:
   background-position moves the crop that `cover` throws away, and
   transform-origin makes the zoom pull toward the same point. These four
   landscapes are 1.56-1.67:1, so on a 16:10 laptop `cover` leaves 0-21px of
   vertical travel and none at all on a phone - without the zoom the framing
   sliders would be dead controls. */
.is-int-theme body[data-route="/interests"] .int-wall-img{
  position: absolute;
  inset: 0;
  background-image: var(--int-wall-url);
  background-position: var(--int-wall-x) var(--int-wall-y);
  background-size: cover;
  background-repeat: no-repeat;
  transform-origin: var(--int-wall-x) var(--int-wall-y);
  scale: var(--int-wall-zoom);
  /* Opacity only. A filter transition on a layer this size is re-rasterised
     every frame, which is what made picking a painting stutter; the script's
     blur-up now fades a pre-blurred bitmap instead. */
  transition: opacity .8s cubic-bezier(.22,.61,.36,1);
}
/* The second layer idles until the script fades it in; an inline opacity from
   JS outranks this, which is how the swap is driven. It is the only layer that
   ever animates, so it is kept on its own compositor layer rather than being
   promoted on the first frame of each fade. */
.is-int-theme body[data-route="/interests"] .int-wall-img[data-layer="b"]{
  opacity: 0;
  will-change: opacity;
}
/* Add .is-enter, let a frame pass, then remove it: the layer blurs up from
   18px as it fades in. Leaving the class off is the safe default, so a layer
   the script never animates is simply visible. */
.is-int-theme body[data-route="/interests"] .int-wall-img.is-enter{
  opacity: 0;
  filter: blur(18px);
}

.is-int-theme body[data-route="/interests"] .int-wall-darken{
  position: absolute;
  inset: 0;
  z-index: 2;
  background: rgb(0 0 0 / var(--int-wall-darken));
}
.is-int-theme body[data-route="/interests"] .int-wall-tint{
  position: absolute;
  inset: 0;
  z-index: 3;
  background: var(--int-tint);
  opacity: var(--int-tint-a);
  mix-blend-mode: var(--int-blend, multiply);
}
.is-int-theme body[data-route="/interests"] .int-wall-vignette{
  position: absolute;
  inset: 0;
  z-index: 4;
  background: radial-gradient(ellipse at center,
              rgb(0 0 0 / 0) 40%, rgb(0 0 0 / var(--int-vignette)) 100%);
}

/* The effect canvas sits above the painting but below darken, tint and
   vignette - hence the explicit z-indexes above, which override DOM order.
   Blur and grayscale stay live on the canvas itself so those three sliders
   keep working while an effect is running, rather than becoming dead
   controls the way they do on the reference. */
.is-int-theme body[data-route="/interests"] .int-wall-canvas:not([hidden]){
  display: block;
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  filter: blur(var(--int-wall-blur)) grayscale(var(--int-wall-gray));
}


/* ============================================
   READING COLUMN
   .int-sheet wraps the existing .wrap.int-shell. The fill is its own absolute
   layer so that backdrop-filter and the column effect canvas never apply to
   the text. Nothing here becomes a scroll container: js/interests.js reads
   window.scrollY and document.body.scrollHeight for the scroll-spy, the
   thread fill and goTo, and every one of them breaks if the column scrolls.
   ============================================ */
.is-int-theme body[data-route="/interests"] .interests{
  padding-top: 0;          /* the fill must start at exactly --int-col-start */
  padding-bottom: 0;       /* moved onto .int-sheet so the fill covers it */
  border-bottom: none;     /* .section's hairline would cut across the painting */
}
/* --int-col-top is where the fill actually begins. It is --int-col-start
   everywhere except phones, which scale it down (see the responsive block). */
.is-int-theme body[data-route="/interests"] .int-sheet{
  --int-col-top: var(--int-col-start);
  position: relative;
  padding-top: calc(var(--int-col-top) + 32px);
  padding-bottom: 96px;
}

/* z-index -1 again: the fill is a positioned sibling that precedes the text in
   the DOM, and a positioned element with z-index auto paints above static
   content. It lands above the wallpaper (earlier in the document at the same
   depth) and below every line of type. */
.is-int-theme body[data-route="/interests"] .int-sheet-fill{
  position: absolute;
  z-index: -1;
  top: var(--int-col-top);
  bottom: 0;
  left: 0;
  right: 0;
  margin-inline: auto;
  /* Edge blur widens the fill by 2xN and feathers both sides; the text keeps
     its own measure so nothing moves when the slider does. */
  width: min(calc(var(--int-col-w) + 2 * var(--int-col-edge)), 100vw);
  /* No overflow clipping here. An overflow other than visible makes this the
     scroll container for the sticky effect canvas inside it - and since this
     box never scrolls, the canvas stopped following the viewport and the
     effects only painted across the first screenful of the column. The edge
     blur feathers with a mask rather than a clip, so nothing needed it. */
  pointer-events: none;
  background: var(--int-col-fill);
  background: color-mix(in srgb, var(--int-col-fill) var(--int-col-a), transparent);
  -webkit-backdrop-filter: blur(var(--int-col-blur));
  backdrop-filter: blur(var(--int-col-blur));
  -webkit-mask-image: linear-gradient(90deg, transparent 0,
              #000 var(--int-col-edge),
              #000 calc(100% - var(--int-col-edge)), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0,
              #000 var(--int-col-edge),
              #000 calc(100% - var(--int-col-edge)), transparent 100%);
}

/* The column's Blend. Inside the fill a colour cannot mix with the painting:
   the glass (backdrop-filter, mask, z-index) makes the fill an isolated group
   whose backdrop starts empty. So while a blend is chosen the colour leaves
   the fill for this layer just under it - same box, same feathered edges -
   where it mixes into the painting, and the glass above blurs the result.
   The texture and any effect stay in the fill, over it, as before. None
   leaves the fill exactly as it was. Not in forced colours, where the
   column is the system's own background. */
.is-int-theme body[data-route="/interests"] .int-sheet-blend{ display: none }
@media not all and (forced-colors: active){
  .is-int-theme.is-col-blended body[data-route="/interests"] .int-sheet-blend,
  .is-int-theme body.is-col-blended[data-route="/interests"] .int-sheet-blend{
    display: block;
    position: absolute;
    z-index: -1;
    top: var(--int-col-top);
    bottom: 0;
    left: 0;
    right: 0;
    margin-inline: auto;
    width: min(calc(var(--int-col-w) + 2 * var(--int-col-edge)), 100vw);
    pointer-events: none;
    background: var(--int-col-fill);
    background: color-mix(in srgb, var(--int-col-fill) var(--int-col-a), transparent);
    mix-blend-mode: var(--int-col-blend, normal);
    -webkit-mask-image: linear-gradient(90deg, transparent 0,
                #000 var(--int-col-edge),
                #000 calc(100% - var(--int-col-edge)), transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0,
                #000 var(--int-col-edge),
                #000 calc(100% - var(--int-col-edge)), transparent 100%);
  }
  .is-int-theme.is-col-blended body[data-route="/interests"] .int-sheet-fill,
  .is-int-theme body.is-col-blended[data-route="/interests"] .int-sheet-fill{ background: transparent }
}
.is-int-theme.is-col-off body[data-route="/interests"] .int-sheet-blend,
.is-int-theme body.is-col-off[data-route="/interests"] .int-sheet-blend{ display: none }

/* Paper texture: the same procedural grain as the Paper effect, warm cream,
   multiplied into the fill. ::before rather than ::after so the column effect
   canvas still paints over it. No external PNG - the reference's 1086x1448
   image is stretched 16x down a document-tall band and shows no fibre at all. */
.is-int-theme body[data-route="/interests"] .int-sheet-fill::before{
  content: '';
  position: absolute;
  inset: 0;
  background-color: #f3e8d6;
  background-image: var(--int-grain);
  background-size: 180px 180px;
  background-repeat: repeat;
  mix-blend-mode: multiply;
  opacity: var(--int-col-texture, 0);
  pointer-events: none;
}
.is-int-theme.is-paper body[data-route="/interests"] .int-sheet-fill::before,
.is-int-theme body.is-paper[data-route="/interests"] .int-sheet-fill::before{ opacity: 1 }

/* Darken and Grayscale, the wallpaper's two for the column. A filter on the
   fill reaches everything the column shows - its colour, the painting seen
   through its glass (Chrome filters the backdrop-filter's output with the
   rest), the paper texture and any effect canvas - and never the text, which
   is not inside it. brightness(100% - d) is the wallpaper's black layer at
   alpha d, the same arithmetic. Only while one of them is up: the script sets
   .is-col-filtered, so the default column carries no filter at all. */
.is-int-theme.is-col-filtered body[data-route="/interests"] .int-sheet-fill,
.is-int-theme body.is-col-filtered[data-route="/interests"] .int-sheet-fill{
  filter: grayscale(var(--int-col-gray)) brightness(calc(100% - var(--int-col-darken)));
}

/* Sticky, not document-tall: the canvas covers only the visible slice of the
   column, so an effect samples the wallpaper in screen space and lines up
   with the painting behind it. */
.is-int-theme body[data-route="/interests"] .int-sheet-canvas:not([hidden]){
  display: block;
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  pointer-events: none;
}

/* Column off means the text moves onto the painting, not that the article
   disappears. --int-ink is re-pointed on .int-sheet rather than on <html>
   because the theme script owns html's inline style and would outrank us
   there; a descendant rule wins cleanly. */
.is-int-theme.is-col-off body[data-route="/interests"] .int-sheet-fill,
.is-int-theme body.is-col-off[data-route="/interests"] .int-sheet-fill{ display: none }
.is-int-theme.is-col-off body[data-route="/interests"] .int-sheet,
.is-int-theme body.is-col-off[data-route="/interests"] .int-sheet{
  --int-ink: var(--int-ink-light);
}
.is-int-theme.is-col-off body[data-route="/interests"] .int-main,
.is-int-theme body.is-col-off[data-route="/interests"] .int-main{
  text-shadow: var(--int-shadow-light);
}


/* ---- Position: where the column stands on a wide screen ----
   Left or right of centre from 1260px up, where the page has room for the
   column, a gutter for the index card or the tools, and a good part of the
   painting beside it; narrower screens always centre it. The fill, its blend
   layer (and the effect canvas inside the fill) and the text all shift by
   the same amount, so they stay in register, and the text keeps its measure.
   The shift is worked out from 50vw for all of them, so the scrollbar -
   counted in vw but not in the layout - moves them together. Relative
   offsets rather than transforms: a transform would make the column the
   containing block of every fixed element inside it. Never past the centre,
   however wide the column. */
@media (min-width: 1260px){
  .is-int-theme body[data-route="/interests"] .int-sheet{
    --int-col-half: calc(var(--int-col-w) / 2 + var(--int-col-edge));
    --int-col-shift: 0px;
  }
  /* clear of the index card when it opens (24px in, 224px wide) */
  .is-int-theme.is-col-left body[data-route="/interests"] .int-sheet{
    --int-col-shift: min(0px, calc(288px + var(--int-col-half) - 50vw));
  }
  /* clear of the tools down the right edge */
  .is-int-theme.is-col-right body[data-route="/interests"] .int-sheet{
    --int-col-shift: max(0px, calc(50vw - 150px - var(--int-col-half)));
  }
  /* and of the settings panel while it is open (300px, 48px in) */
  .is-int-theme.is-col-right.is-panel-open body[data-route="/interests"] .int-sheet{
    --int-col-shift: max(0px, calc(50vw - 380px - var(--int-col-half)));
  }
  .is-int-theme body[data-route="/interests"] .int-sheet-fill,
  .is-int-theme.is-col-blended body[data-route="/interests"] .int-sheet-blend,
  .is-int-theme body.is-col-blended[data-route="/interests"] .int-sheet-blend{
    left: var(--int-col-shift);
    right: calc(-1 * var(--int-col-shift));
  }
  .is-int-theme body[data-route="/interests"] .int-main{
    position: relative;
    left: var(--int-col-shift);
  }
}


/* ============================================
   READING COLUMN - TYPE
   The existing Interests rules size everything in rem, which no slider can
   reach. Every one of them is restated here in em against .int-main's own
   font-size, using the identical numbers, so the default rendering is the
   design that is already on the page and the Size slider scales the whole
   column from one value. Line heights are restated as a fraction of
   --int-line (original / 1.75) so the Line height slider moves every block
   while keeping the proportions the page was drawn with.
   ============================================ */
.is-int-theme body[data-route="/interests"] .int-shell{
  /* The index is fixed to the viewport at every width (desktop card, tablet
     bar, phone bar), so it takes no room in the layout and the shell is one
     column, no longer capped at 1200px. The reading column is centred on the
     viewport by .int-main below. */
  max-width: none;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}
.is-int-theme body[data-route="/interests"] .int-main{
  /* Centred on its own. It used to reserve the index's 200px column plus a
     72px gap and mirror that back as padding, which squeezed the text to
     ~390px between 981 and 1130px wide. */
  max-width: var(--int-measure);
  padding-right: 0;
  /* width:100% matters as much as the max-width. An auto inline margin
     suppresses a grid item's stretch, which leaves the box sized to its
     content - and content wider than the track then overflows it, putting
     lines of type out on the bare painting at phone widths. Stretching to the
     track first and capping afterwards keeps the text inside the column at
     every width, and the mirrored padding still centres it either way. */
  width: 100%;
  margin-inline: auto;
  font-size: var(--int-size);
  font-weight: var(--int-weight);
  line-height: var(--int-line);
  letter-spacing: var(--int-track);
  color: var(--int-ink);
}

/* Typefaces. Each rule falls back to the family the page already used, so
   removing --int-font restores the original mixture of Playfair, EB Garamond
   and Inter instead of flattening the column to one face. */
.is-int-theme body[data-route="/interests"] :is(.interests-title, .int-group-title){
  font-family: var(--int-font-head, var(--int-font, 'Playfair Display', Georgia, 'Times New Roman', serif));
}
/* A pairing's display faces mostly come in one or two weights: the Weight
   slider picks among those rather than smearing a fake bold over them. */
.is-int-theme.is-face-head body[data-route="/interests"]
  :is(.interests-title, .int-group-title, .int-intro-name, .book-title, .book-opinion){
  font-synthesis: none;
}
.is-int-theme body[data-route="/interests"]
  :is(.int-name, .film-title, .artist, .piece-title, .int-excerpt p,
      .int-quote blockquote p, .int-link-handle){
  font-family: var(--int-font, 'EB Garamond', Georgia, serif);
}
.is-int-theme body[data-route="/interests"]
  :is(.int-eyebrow, .int-count, .int-group-blurb, .int-meta, .int-note, .int-sub,
      .film-year, .piece-by, .int-quote cite, .friend-name, .int-link-name){
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
}

/* Headers answer to their own scale and weight sliders. */
.is-int-theme body[data-route="/interests"] .interests-title{
  font-size: calc(clamp(2.1em, 4vw, 2.9em) * var(--int-head-scale));
  line-height: calc(var(--int-line) * .63);
  font-weight: var(--int-head-weight);
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] .int-group-title{
  font-size: calc(1.55em * var(--int-head-scale));
  line-height: calc(var(--int-line) * .69);
  font-weight: var(--int-head-weight);
  color: var(--int-ink);
}

/* Body sizes, one declaration per original rem value. */
.is-int-theme body[data-route="/interests"] .int-eyebrow{ font-size: .69em }
.is-int-theme body[data-route="/interests"] .int-count{ font-size: .69em }
.is-int-theme body[data-route="/interests"] .int-group-blurb{
  font-size: .86em;
  line-height: calc(var(--int-line) * .86);
}
.is-int-theme body[data-route="/interests"] .int-name{
  font-size: 1.24em;
  line-height: calc(var(--int-line) * .77);
  /* the page sets 500 here; keep it one step above whatever the body weight
     slider is on, so Bold still reads as bold against the running text */
  font-weight: calc(var(--int-weight) + 100);
}
.is-int-theme body[data-route="/interests"] .int-meta{ font-size: .68em }
.is-int-theme body[data-route="/interests"] .int-note{
  font-size: .93em;
  line-height: calc(var(--int-line) * .94);
}
.is-int-theme body[data-route="/interests"] .int-row-go{ font-size: .95em }
.is-int-theme body[data-route="/interests"] .int-sub{ font-size: .68em }
.is-int-theme body[data-route="/interests"] .film-title{
  font-size: 1.1em;
  line-height: calc(var(--int-line) * .8);
}
.is-int-theme body[data-route="/interests"] .film-year{ font-size: .74em }
.is-int-theme body[data-route="/interests"] .artist{
  font-size: 1.18em;
  line-height: calc(var(--int-line) * .8);
}
.is-int-theme body[data-route="/interests"] .piece-title{
  font-size: 1.12em;
  line-height: calc(var(--int-line) * .8);
}
.is-int-theme body[data-route="/interests"] .piece-by{ font-size: .74em }
.is-int-theme body[data-route="/interests"] .piece .int-note{ font-size: .88em }
.is-int-theme body[data-route="/interests"] .int-excerpt p{
  font-size: 1.18em;
  line-height: var(--int-line);
}
.is-int-theme body[data-route="/interests"] #int-poetry .int-excerpt p{
  font-size: 1.22em;
  line-height: calc(var(--int-line) * 1.03);
}
.is-int-theme body[data-route="/interests"] .int-excerpt + .int-note{ font-size: .86em }
.is-int-theme body[data-route="/interests"] .int-quote blockquote p{
  font-size: 1.32em;
  line-height: calc(var(--int-line) * .94);
}
.is-int-theme body[data-route="/interests"] .int-quote cite{ font-size: .74em }
/* The Latin inscriptions keep their Roman capitals whatever typeface the
   visitor picks: the letterforms are the point. They follow the size, line
   and ink sliders like everything else, and stay upright under the italic
   toggle, since a carved letter does not slant. */
.is-int-theme body[data-route="/interests"] .int-quote .int-latin{
  font-size: 1em;
  line-height: inherit;
  font-style: normal;
}
.is-int-theme body[data-route="/interests"] .int-latin-carved{
  font-size: 1.22em;
  line-height: calc(var(--int-line) * .86);
  font-style: normal;
  color: color-mix(in srgb, var(--int-ink) 80%, transparent);
  /* the lit lip of the cut is a lighter shade of the column itself, so on a
     dark fill it stays a faint edge instead of a white double */
  text-shadow:
    0 1px 0 color-mix(in srgb, color-mix(in srgb, var(--int-col-fill), white 55%) 70%, transparent),
    0 -1px 0 rgb(0 0 0 / .16);
}
.is-int-theme.is-col-off body[data-route="/interests"] .int-latin-carved,
.is-int-theme body.is-col-off[data-route="/interests"] .int-latin-carved{
  text-shadow: var(--int-shadow-light);
}
.is-int-theme body[data-route="/interests"] .int-quote .int-gloss{
  font-family: var(--int-font, 'EB Garamond', Georgia, serif);
  font-size: 1.08em;
  line-height: calc(var(--int-line) * .89);
  color: color-mix(in srgb, var(--int-ink) 62%, transparent);
}
.is-int-theme body[data-route="/interests"] .game .int-name{ font-size: 1.16em }
.is-int-theme body[data-route="/interests"] .game .int-note{ font-size: .9em }
.is-int-theme body[data-route="/interests"] .friend-name{ font-size: .9em }
.is-int-theme body[data-route="/interests"] .proj-head .int-name{ font-size: 1.12em }
.is-int-theme body[data-route="/interests"] .proj-body .int-note{ font-size: .88em }
.is-int-theme body[data-route="/interests"] .int-link-name{
  font-size: .92em;
  line-height: calc(var(--int-line) * .8);
}
.is-int-theme body[data-route="/interests"] .int-link-handle{ font-size: .98em }
/* These two carry overflow:hidden and an ellipsis already, but nothing was
   ever constraining their width: at 480 and below the row stacks into a
   column flex box aligned on the baseline, which sizes each line to its own
   content instead of stretching it, so a long handle simply grew past the
   page. On white that was invisible; over a painting the tail of the line
   lands on bare rock outside the reading column, so the cap is needed here
   even though the measure is doing its job. */
.is-int-theme body[data-route="/interests"] :is(.int-link-name, .int-link-handle){
  max-width: 100%;
}
.is-int-theme body[data-route="/interests"] .int-link-go{ font-size: .95em }

/* Books. Each face falls back to the one the Books tab used: Georgia for the
   epigraph, Playfair italic for the titles, Inter for the rest. The cover is
   in em too, so the Size slider scales the shelf as one piece. */
.is-int-theme body[data-route="/interests"] .book-quote p{
  font-family: var(--int-font, Georgia, 'Times New Roman', serif);
  font-size: 1.2em;
  line-height: calc(var(--int-line) * .91);
}
.is-int-theme body[data-route="/interests"] .book-title{
  font-family: var(--int-font-head, var(--int-font, 'Playfair Display', Georgia, 'Times New Roman', serif));
  font-size: 1.42em;
  line-height: calc(var(--int-line) * .74);
  /* 400 on the page; it follows the body weight, like the names do */
  font-weight: var(--int-weight);
}
.is-int-theme body[data-route="/interests"] :is(.book-author, .book-quote cite, .book-opinion p, .book-note p){
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
}
.is-int-theme body[data-route="/interests"] .book-author{ font-size: .9em }
.is-int-theme body[data-route="/interests"] .book-quote cite{ font-size: .82em }
.is-int-theme body[data-route="/interests"] :is(.book-opinion p, .book-note p){
  font-size: .95em;
  line-height: calc(var(--int-line) * .97);
}
.is-int-theme body[data-route="/interests"] .book-opinion::before{
  font-family: var(--int-font-head, var(--int-font, 'Playfair Display', Georgia, serif));
  font-size: 3em;
}
.is-int-theme body[data-route="/interests"] .book-cover{ width: 8.5em }

/* The intro keeps the sans it had on the Experience page and answers to the
   header sliders; the roles take the serif the other entries use. */
.is-int-theme body[data-route="/interests"] .int-intro-name{
  font-family: var(--int-font-head, var(--int-font, 'Inter', system-ui, sans-serif));
  font-size: calc(2.125em * var(--int-head-scale));
  line-height: calc(var(--int-line) * .66);
  font-weight: calc(var(--int-head-weight) + 100);
}
.is-int-theme body[data-route="/interests"] :is(.int-intro-tagline, .int-intro-links a, .xp-org, .xp-meta, .xp-desc){
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
}
.is-int-theme body[data-route="/interests"] .int-intro-tagline{ font-size: 1.125em; line-height: calc(var(--int-line) * .91) }
.is-int-theme body[data-route="/interests"] .int-intro-links a{ font-size: .95em; line-height: calc(var(--int-line) * .86) }
/* The tree at 2 screen pixels to each of its own (142px at 16px), standing on
   the hairline with its crown level with the name: both in em, so the Size
   slider keeps the fit. */
.is-int-theme body[data-route="/interests"] .int-intro{ --intro-ground: 1.6875em }
.is-int-theme body[data-route="/interests"] .int-intro-tree img{ height: 8.875em }
.is-int-theme body[data-route="/interests"] .xp-role{
  font-family: var(--int-font, 'EB Garamond', Georgia, serif);
  font-size: 1.24em;
  line-height: calc(var(--int-line) * .77);
  font-weight: calc(var(--int-weight) + 100);
}
.is-int-theme body[data-route="/interests"] .xp-org{
  font-size: .93em;
  line-height: calc(var(--int-line) * .86);
  font-weight: calc(var(--int-weight) + 100);
}
.is-int-theme body[data-route="/interests"] .xp-meta{ font-size: .68em }
.is-int-theme body[data-route="/interests"] .xp-desc{ font-size: .93em; line-height: calc(var(--int-line) * .94) }

/* The note from me, and today's footnote under it, restated in em and in the
   column's ink like the rest. */
.is-int-theme body[data-route="/interests"] .int-hello{
  max-width: none;
  font-family: var(--int-font, 'EB Garamond', Georgia, serif);
  font-size: 1.2em;
  line-height: calc(var(--int-line) * .93);
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] .int-hello a{
  text-decoration-color: color-mix(in srgb, var(--int-ink) 35%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-hello a:hover, .int-hello a:focus-visible){
  text-decoration-color: color-mix(in srgb, var(--int-ink) 75%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-daily{ max-width: none }
.is-int-theme body[data-route="/interests"] :is(.int-daily-label, .int-daily-next){
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
  font-size: .69em;
  color: color-mix(in srgb, var(--int-ink) 45%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-daily-next{
  text-decoration-color: color-mix(in srgb, var(--int-ink) 22%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-daily-text{
  font-family: var(--int-font, 'EB Garamond', Georgia, serif);
  font-size: 1.14em;
  line-height: calc(var(--int-line) * .92);
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] .int-daily-go{
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
  font-size: .86em;
  color: color-mix(in srgb, var(--int-ink) 72%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-daily-go-name{
  text-decoration-color: color-mix(in srgb, var(--int-ink) 30%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-daily-go-where, .int-daily-go-arrow){
  color: color-mix(in srgb, var(--int-ink) 45%, transparent);
}
@media (hover: hover){
  .is-int-theme body[data-route="/interests"] .int-daily-next:hover{
    color: color-mix(in srgb, var(--int-ink) 80%, transparent);
    text-decoration-color: color-mix(in srgb, var(--int-ink) 50%, transparent);
  }
  .is-int-theme body[data-route="/interests"] .int-daily-go:hover .int-daily-go-name{
    text-decoration-color: color-mix(in srgb, var(--int-ink) 70%, transparent);
  }
}
.is-int-theme body[data-route="/interests"] .is-flagged{
  --flag: color-mix(in srgb, var(--int-ink) 9%, transparent);
}

/* Aa and I. The page pins font-style:normal on several elements so italic
   stays an accent; the toggle has to lift those pins or it only reaches half
   the column. */
.is-int-theme.is-caps body[data-route="/interests"] .int-main,
.is-int-theme body.is-caps[data-route="/interests"] .int-main{ text-transform: uppercase }
.is-int-theme.is-italic body[data-route="/interests"]
  :is(.int-main, .int-name, .int-group-title, .interests-title, .int-excerpt p, .int-quote cite, .book-quote cite),
.is-int-theme body.is-italic[data-route="/interests"]
  :is(.int-main, .int-name, .int-group-title, .interests-title, .int-excerpt p, .int-quote cite, .book-quote cite){
  font-style: italic;
}


/* ============================================
   READING COLUMN - INK
   Every literal ink value in the Interests block is re-expressed against
   --int-ink so one colour picker drives the whole column, dark ink on a pale
   fill or light ink on a dark one. color-mix carries the original alphas.
   ============================================ */
.is-int-theme body[data-route="/interests"]
  :is(.int-group-title, .int-name, .int-link-name, .film-title, .artist,
      .piece-title, .int-excerpt p, .int-quote blockquote p, .int-note,
      .friend-name, .interests-title){
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] .int-count{
  color: color-mix(in srgb, var(--int-ink) 32%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-eyebrow{
  color: color-mix(in srgb, var(--int-ink) 40%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-sub{
  color: color-mix(in srgb, var(--int-ink) 42%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-meta, .film-year){
  color: color-mix(in srgb, var(--int-ink) 45%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.piece-by, .int-quote cite){
  color: color-mix(in srgb, var(--int-ink) 48%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-link-icon, .int-link-handle){
  color: color-mix(in srgb, var(--int-ink) 50%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-group-blurb, .int-excerpt + .int-note){
  color: color-mix(in srgb, var(--int-ink) 55%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-row-go, .proj-go, .int-link-go){
  color: color-mix(in srgb, var(--int-ink) 28%, transparent);
}
.is-int-theme body[data-route="/interests"]
  :is(a.int-row:hover .int-row-go, a.int-row:focus-visible .int-row-go,
      .proj-card:hover .proj-go, .int-link:hover .int-link-go,
      .int-link:hover .int-link-icon, .film:hover .film-title){
  color: var(--int-ink);
}

/* Rules, borders and leaders follow the ink as well, or a dark preset leaves
   a grid of pale hairlines floating over the painting. */
.is-int-theme body[data-route="/interests"] .piece{
  border-bottom-color: color-mix(in srgb, var(--int-ink) 12%, transparent);
}
.is-int-theme body[data-route="/interests"]
  :is(.int-item, .int-games .game, .game-art, .int-links li){
  border-color: color-mix(in srgb, var(--int-ink) 14%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-group-head{
  border-bottom-color: color-mix(in srgb, var(--int-ink) 20%, transparent);
}
.is-int-theme body[data-route="/interests"] .proj-shot{
  border-color: color-mix(in srgb, var(--int-ink) 18%, transparent);
}
.is-int-theme body[data-route="/interests"] .proj-card:hover .proj-shot{
  border-color: color-mix(in srgb, var(--int-ink) 40%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-excerpt{
  border-left-color: color-mix(in srgb, var(--int-ink) 26%, transparent);
}
.is-int-theme body[data-route="/interests"] .film::after{
  border-bottom-color: color-mix(in srgb, var(--int-ink) 26%, transparent);
}
.is-int-theme body[data-route="/interests"] .film:hover::after{
  border-bottom-color: color-mix(in srgb, var(--int-ink) 50%, transparent);
}
.is-int-theme body[data-route="/interests"]
  :is(a.int-row:hover .int-name, a.proj-card:hover .int-name){
  text-decoration-color: color-mix(in srgb, var(--int-ink) 45%, transparent);
}

/* Books, on the same ink. The highlighter stays amber: it is the section's
   one accent, and at this alpha it reads under dark ink on a pale fill and
   as a soft glow under light ink on the Dark preset. */
.is-int-theme body[data-route="/interests"] :is(.book-title, .book-quote, .book-opinion p, .book-note p){
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] :is(.book-author, .book-quote cite){
  color: color-mix(in srgb, var(--int-ink) 72%, transparent);
}
.is-int-theme body[data-route="/interests"] .book-opinion::before{
  color: color-mix(in srgb, var(--int-ink) 20%, transparent);
}
.is-int-theme body[data-route="/interests"] .book-quote{
  border-left-color: color-mix(in srgb, var(--int-ink) 30%, transparent);
}
.is-int-theme body[data-route="/interests"] .book{
  border-bottom-color: color-mix(in srgb, var(--int-ink) 14%, transparent);
}
.is-int-theme body[data-route="/interests"] .book-cover{
  background: color-mix(in srgb, var(--int-ink) 8%, transparent);
}

/* The intro and the work experience, on the same ink. */
.is-int-theme body[data-route="/interests"] :is(.int-intro-name, .xp-role, .xp-desc){ color: var(--int-ink) }
.is-int-theme body[data-route="/interests"] .int-intro-tagline{ color: color-mix(in srgb, var(--int-ink) 62%, transparent) }
.is-int-theme body[data-route="/interests"] .int-intro-links a{
  color: color-mix(in srgb, var(--int-ink) 80%, transparent);
  text-decoration-color: color-mix(in srgb, var(--int-ink) 32%, transparent);
}
.is-int-theme body[data-route="/interests"] :is(.int-intro-links a:hover, .int-intro-links a:focus-visible){
  color: var(--int-ink);
  text-decoration-color: color-mix(in srgb, var(--int-ink) 70%, transparent);
}
.is-int-theme body[data-route="/interests"] .xp-org{ color: color-mix(in srgb, var(--int-ink) 85%, transparent) }
.is-int-theme body[data-route="/interests"] .xp-meta{ color: color-mix(in srgb, var(--int-ink) 50%, transparent) }
.is-int-theme body[data-route="/interests"] .int-intro{ border-bottom-color: color-mix(in srgb, var(--int-ink) 20%, transparent) }
.is-int-theme body[data-route="/interests"] .xp{ border-bottom-color: color-mix(in srgb, var(--int-ink) 14%, transparent) }
.is-int-theme body[data-route="/interests"] .xp-banner{ background: color-mix(in srgb, var(--int-ink) 8%, transparent) }

/* The hover preview is a floating card, so it takes the column's surface
   rather than the painting, and drops under the settings panel. */
.is-int-theme body[data-route="/interests"] .int-preview{
  z-index: 1080;
  background: var(--int-col-fill);
  border-color: color-mix(in srgb, var(--int-ink) 18%, transparent);
  box-shadow: 0 14px 34px rgb(0 0 0 / .28);
}
.is-int-theme body[data-route="/interests"] :is(.int-preview-name, .int-preview-mark){
  color: var(--int-ink);
}
.is-int-theme body[data-route="/interests"] .int-preview-mark{
  background: color-mix(in srgb, var(--int-ink) 6%, transparent);
  border-color: color-mix(in srgb, var(--int-ink) 16%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-preview-meta{
  color: color-mix(in srgb, var(--int-ink) 45%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-preview-host{
  color: color-mix(in srgb, var(--int-ink) 55%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-preview-shot{
  background: color-mix(in srgb, var(--int-ink) 6%, var(--int-col-fill));
  border-bottom-color: color-mix(in srgb, var(--int-ink) 14%, transparent);
  color: color-mix(in srgb, var(--int-ink) 24%, transparent);
}
/* The card is chrome, so its ink answers to its own solid fill rather than to
   the column's: with the column off the page ink turns light for the bare
   painting, which left cream text on a white card, and a hand-picked ink is
   matched to the translucent column, not to an opaque one. Near-black on a
   light fill, near-white on a dark one; every rule above reads --int-ink, so
   this one declaration re-inks the whole card. */
@supports (color: oklch(from white l c h)){
  .is-int-theme body[data-route="/interests"] .int-preview{
    --int-ink: oklch(from var(--int-col-fill) calc(.26 + .7 * clamp(0, (.6 - l) * 1000, 1)) 0 0);
  }
}


/* ============================================
   DISTRESS
   A tiled fractal-noise mask plus a solid layer at alpha (1 - distress),
   composited with add: at 0% the solid layer is opaque and nothing changes,
   and as it rises only the noise keeps pixels alive, which erodes the inside
   of each letter the way worn letterpress does. It is applied to text
   containers only - never to .proj-shot img, .game-art img or the preview
   card, which the reference speckles along with everything else.
   ============================================ */
/* The script marks the elements: it walks the same list of text containers but
   drops any that contain an <img>, so game covers and project shots keep their
   pixels. Masking the list from here as well put a mask layer on 385 elements
   at all times, and the two mechanisms eroded the text twice over. */
.is-int-theme body[data-route="/interests"] .int-distress{
  -webkit-mask-image: var(--int-noise),
    linear-gradient(rgb(0 0 0 / calc(100% - var(--int-distress))),
                    rgb(0 0 0 / calc(100% - var(--int-distress))));
  -webkit-mask-size: 220px 220px, 100% 100%;
  -webkit-mask-repeat: repeat, no-repeat;
  -webkit-mask-composite: source-over;
  mask-image: var(--int-noise),
    linear-gradient(rgb(0 0 0 / calc(100% - var(--int-distress))),
                    rgb(0 0 0 / calc(100% - var(--int-distress))));
  mask-size: 220px 220px, 100% 100%;
  mask-repeat: repeat, no-repeat;
  mask-composite: add;
}


/* ============================================
   ROUTE-SCOPED PAGE CHROME
   Everything the page draws in white was tuned for a white page. Over a
   painting each one reads as a panel of fog, so they either go transparent or
   turn into glass. Follows the body[data-route="/timeline"] precedent already
   in styles.css.
   ============================================ */
.is-int-theme body[data-route="/interests"] .hero{ border-bottom: none }

/* The footer sits below the fill, straight on the painting, so it takes the
   same light ink the index does. */
.is-int-theme body[data-route="/interests"] .footer{
  background: transparent;
  color: var(--int-ink-light);
  text-shadow: var(--int-shadow-light);
}
.is-int-theme body[data-route="/interests"] .footer a{ color: var(--int-ink-light) }



/* ============================================
   TOOLS RAIL
   Three buttons at the right edge, level with the navigation islands at the
   left. z 1100 puts the rail over the islands (900) and the panel (1090) but
   under the phone dock (10000).
   ============================================ */
.is-int-theme body[data-route="/interests"] .int-tools{
  position: fixed;
  z-index: 1100;
  top: 24px;
  right: 0;
  bottom: 0;
  width: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 0;
  background: var(--int-ui-glass);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  backdrop-filter: blur(8px) saturate(140%);
  border-left: 1px solid var(--int-ui-line);
}
.is-int-theme body[data-route="/interests"] .int-tool{
  position: relative;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  min-width: 0;
  min-height: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: #222;
  transition: background .15s ease, color .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-tool svg{
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.is-int-theme body[data-route="/interests"] .int-tool:hover{ background: rgb(15 23 42 / .07) }
.is-int-theme body[data-route="/interests"] .int-tool[aria-pressed="true"]{
  background: rgb(15 23 42 / .11);
  color: #0f172a;
}

/* Tooltips open away from the edge they are docked against. They are inside
   the button, so the rail must never clip its overflow. */
.is-int-theme body[data-route="/interests"] .int-tool-tip{
  position: absolute;
  top: 50%;
  right: calc(100% + 12px);
  translate: 4px -50%;
  padding: 5px 9px;
  border-radius: 7px;
  background: rgb(28 28 30 / .92);
  color: #fff;
  font: 500 12px/1.2 'Inter', system-ui, sans-serif;
  letter-spacing: .01em;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .16s cubic-bezier(.22,1,.36,1),
              translate .18s cubic-bezier(.22,1,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-tool:focus-visible .int-tool-tip{
  opacity: 1;
  translate: 0 -50%;
}
/* Hover only where hover is real: touch screens keep :hover on whatever was
   tapped last, which left the label stuck over the open panel's header. */
@media (hover: hover) and (pointer: fine){
  .is-int-theme body[data-route="/interests"] .int-tool:hover .int-tool-tip{
    opacity: 1;
    translate: 0 -50%;
  }
}
/* The open tool needs no label - it would sit over its own panel. */
.is-int-theme body[data-route="/interests"] .int-tool[aria-pressed="true"] .int-tool-tip{ opacity: 0 }


/* ============================================
   SETTINGS PANEL
   One panel, docked against the inner edge of the rail, with three panes that
   swap instantly - the fade and slide belong to open and close only.
   @starting-style animates the open; there is no close animation because the
   script sets [hidden], which removes the box in the same frame.
   ============================================ */
.is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
  position: fixed;
  z-index: 1090;
  top: 24px;
  right: 48px;
  bottom: 0;
  width: 300px;
  display: flex;
  flex-direction: column;
  padding: 18px 20px;
  background: var(--int-ui-glass);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  backdrop-filter: blur(8px) saturate(140%);
  border-left: 1px solid var(--int-ui-line);
  color: var(--int-ui-ink);
  font-family: 'Inter', system-ui, sans-serif;
  opacity: 1;
  translate: 0;
  transition: opacity .3s ease, translate .3s cubic-bezier(.22,1,.36,1);
}
@starting-style{
  .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
    opacity: 0;
    translate: 12px;
  }
}

.is-int-theme body[data-route="/interests"] .int-panel-head{
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--int-ui-line);
}
.is-int-theme body[data-route="/interests"] .int-panel-title{
  flex: 1 1 auto;
  margin: 0;
  font: 600 14px/1.2 'Inter', system-ui, sans-serif;
  letter-spacing: .02em;
  color: #111827;
}
.is-int-theme body[data-route="/interests"] :is(.int-panel-reset, .int-panel-close){
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  min-width: 0;
  min-height: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--int-ui-ink-soft);
  /* these two carry a text glyph rather than an icon in the markup */
  font: 400 15px/1 'Inter', system-ui, sans-serif;
  transition: background .15s ease, color .15s ease;
}
.is-int-theme body[data-route="/interests"] :is(.int-panel-reset, .int-panel-close):hover{
  background: rgb(15 23 42 / .07);
  color: #111827;
}
.is-int-theme body[data-route="/interests"] :is(.int-panel-reset, .int-panel-close) svg{
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.is-int-theme body[data-route="/interests"] .int-panel-body{
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* 6px of room either side for the focus ring and the selected halo, which
     the scroller would otherwise clip; the negative margin keeps the controls
     where they were */
  padding: 14px 8px 14px 6px;
  margin-inline: -6px;
  scrollbar-width: thin;
  scrollbar-color: rgb(15 23 42 / .16) transparent;
}
.is-int-theme body[data-route="/interests"] .int-panel-body::-webkit-scrollbar{ width: 6px }
.is-int-theme body[data-route="/interests"] .int-panel-body::-webkit-scrollbar-track{ background: transparent }
.is-int-theme body[data-route="/interests"] .int-panel-body::-webkit-scrollbar-thumb{
  background: rgb(15 23 42 / .16);
  border-radius: 3px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body::-webkit-scrollbar-thumb:hover{
  background: rgb(15 23 42 / .28);
}

.is-int-theme body[data-route="/interests"] .int-panel-foot{
  flex: 0 0 auto;
  padding-top: 14px;
  border-top: 1px solid var(--int-ui-line);
}

/* The markup carries a real backdrop element for the phone sheet. There is no
   scrim on desktop - the panel is docked beside the rail and the page behind
   it stays usable - so it is held closed here whatever its [hidden] state,
   and only the bottom-sheet breakpoint gives it a box. */
.is-int-theme body[data-route="/interests"] .int-panel-backdrop{ display: none }

/* Section headings and the vertical rhythm inside a pane. Written on the pane
   rather than on a class of its own so that a group the script renders from a
   template still sits correctly even before it has been given a name. */
.is-int-theme body[data-route="/interests"] .int-pane > * + *{ margin-top: 10px }
.is-int-theme body[data-route="/interests"] .int-sec{ margin-top: 24px }
.is-int-theme body[data-route="/interests"] .int-sec-title{
  display: block;
  margin: 24px 0 10px;
  font: 600 13px/1.3 'Inter', system-ui, sans-serif;
  letter-spacing: .02em;
  color: #111827;
}
.is-int-theme body[data-route="/interests"] .int-pane > .int-sec-title:first-child{ margin-top: 0 }


/* ============================================
   CONTROLS
   Styled from the roles and attributes the DOM contract fixes - role="slider",
   role="switch", aria-checked, aria-pressed - so the look does not depend on
   guessing the class names the script generates. The int- classes are the
   hooks for the parts ARIA cannot describe: the fill bar, the readout, the
   picker grid, the palette strip and the typeface rows.
   ============================================ */

/* Every button inside the panel body shares one resting shape. */
.is-int-theme body[data-route="/interests"] .int-panel-body button{
  min-width: 0;
  min-height: 32px;
  padding: 7px 10px;
  border: 1px solid var(--int-ui-line);
  border-radius: 8px;
  background: rgb(255 255 255 / .5);
  color: #444;
  font: 600 13px/1.2 'Inter', system-ui, sans-serif;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-body button:hover{
  background: rgb(15 23 42 / .06);
  border-color: rgb(15 23 42 / .2);
}
.is-int-theme body[data-route="/interests"]
  .int-panel-body button:is([aria-pressed="true"], [aria-checked="true"], .is-on){
  background: rgb(15 23 42 / .05);
  border-color: rgb(15 23 42 / .55);
  color: #111827;
  font-weight: 600;
}

/* ---- slider ---- */
.is-int-theme body[data-route="/interests"] .int-ctl[data-ctl="slider"]{
  position: relative;
  display: flex;
  align-items: center;
  height: 32px;
  padding: 0 12px;
  border-radius: 8px;
  overflow: hidden;
  background: rgb(15 23 42 / .05);
  /* A vertical swipe is the browser's, so the sheet scrolls under a finger
     that starts on a slider; a sideways drag is the slider's (the script
     waits for the finger to commit to a direction before taking it). */
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}
/* The fill takes its width from an inline style or from --int-ctl-p; either
   way the bar is the only moving part, and there is no thumb to chase. */
.is-int-theme body[data-route="/interests"] .int-ctl-fill{
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--int-ctl-p, 0%);
  background: rgb(15 23 42 / .11);
  pointer-events: none;
}
.is-int-theme body[data-route="/interests"] .int-ctl-label{
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font: 400 12px/1 'Inter', system-ui, sans-serif;
  letter-spacing: .02em;
  color: #444;
}
.is-int-theme body[data-route="/interests"] .int-ctl-val{
  position: relative;
  flex: 0 0 auto;
  margin-left: 8px;
  font: 400 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: #777;
  font-variant-numeric: tabular-nums;
}

/* ---- switch ---- */
.is-int-theme body[data-route="/interests"] .int-ctl[data-ctl="switch"]{
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 32px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch){
  flex: 0 0 auto;
  width: 44px;
  height: 26px;
  min-height: 26px;
  padding: 0;
  border: 0;
  border-radius: 13px;
  background: rgb(15 23 42 / .18);
  transition: background .2s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)::after{
  content: '';
  display: block;
  width: 20px;
  height: 20px;
  margin-left: 3px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgb(0 0 0 / .25);
  translate: 0;
  transition: translate .2s cubic-bezier(.22,1,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)[aria-checked="true"]{
  background: #2563eb;
  border-color: transparent;
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)[aria-checked="true"]::after{
  translate: 18px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch):hover{
  background: rgb(15 23 42 / .26);
}
.is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)[aria-checked="true"]:hover{
  background: #1d4ed8;
}

/* ---- segmented ---- */
.is-int-theme body[data-route="/interests"] :is(.int-seg, [role="radiogroup"]){
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.is-int-theme body[data-route="/interests"] :is(.int-seg, [role="radiogroup"]) button{
  flex: 1 1 auto;
}

/* ---- colour row ---- */
.is-int-theme body[data-route="/interests"] .int-color{
  display: flex;
  align-items: center;
  gap: 10px;
}
.is-int-theme body[data-route="/interests"] .int-color-swatch{
  position: relative;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 7px;
  border: 1px solid rgb(15 23 42 / .18);
  overflow: hidden;
}
/* The native picker is stacked invisibly over the swatch: the platform dialog
   is better than any square we could draw, and it is keyboard-operable for
   free. */
.is-int-theme body[data-route="/interests"] .int-color-swatch input[type="color"]{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  opacity: 0;
}
.is-int-theme body[data-route="/interests"] .int-panel-body input[type="text"]{
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid rgb(15 23 42 / .15);
  border-radius: 7px;
  background: rgb(255 255 255 / .7);
  font: 400 13px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--int-ui-ink);
  text-transform: uppercase;
  transition: border-color .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-body input[type="text"]:is(:hover, :focus){
  border-color: rgb(15 23 42 / .38);
}
/* ============================================
   PICKER (T and W)
   The Omarchy-style carousel js/theme-picker.js opens: the page behind a
   scrim of the theme's background at half strength, the chosen card large
   and slanted with a 3px accent border, the rest as narrow slanted slices
   dimmed by the background at 42% with a 1px border of the foreground at
   28%, and the name underneath in bold monospace, outlined in the
   background at 70% - Omarchy's image picker, colour for colour. The
   script sets --pk-bg, --pk-fg and --pk-accent from the current theme, and
   positions and clips every card itself; this block only paints them. Over
   the top of everything, the phone dock included.
   ============================================ */
#int-picker[hidden]{ display: none !important }
.is-int-theme body[data-route="/interests"] #int-picker{
  --pk-outline: color-mix(in srgb, var(--pk-bg) 70%, transparent);
  position: fixed;
  inset: 0;
  z-index: 20000;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: color-mix(in srgb, var(--pk-bg) 50%, transparent);
  /* Omarchy's scrim falls over desktop windows; here it falls over lines of
     text, right behind the name. A light blur keeps them from reading
     through it. */
  -webkit-backdrop-filter: blur(5px);
  backdrop-filter: blur(5px);
  color: var(--pk-fg);
  font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', Menlo, Consolas, monospace;
  overscroll-behavior: contain;
  transition: opacity .16s ease;
}
.is-int-theme body[data-route="/interests"] #int-picker.is-entering{ opacity: 0 }
.is-int-theme body[data-route="/interests"] .int-picker-card{
  position: relative;
  width: 100%;
  padding-top: var(--pk-top, 30px);
}
.is-int-theme body[data-route="/interests"] .int-picker-strip{
  position: relative;
  width: 100%;
  outline: none;
}
.is-int-theme body[data-route="/interests"] .int-picker-item{
  position: absolute;
  left: 0;
  top: 0;
  /* the border: this layer shows only where the picture is clipped in */
  background: color-mix(in srgb, var(--pk-fg) 28%, transparent);
  transition: transform .22s cubic-bezier(.22,.61,.36,1), width .22s cubic-bezier(.22,.61,.36,1),
              height .22s cubic-bezier(.22,.61,.36,1), clip-path .22s cubic-bezier(.22,.61,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-picker-item.is-chosen{ background: var(--pk-accent) }
.is-int-theme body[data-route="/interests"] .int-picker-shot{
  position: absolute;
  inset: 0;
  background-color: var(--pk-bg);
  background-position: center;
  background-size: cover;
  transition: clip-path .22s cubic-bezier(.22,.61,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-picker-shot img{
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  user-select: none;
}
.is-int-theme body[data-route="/interests"] .int-picker-dim{
  position: absolute;
  inset: 0;
  background: var(--pk-bg);
  opacity: .42;
  transition: opacity .22s ease;
}
.is-int-theme body[data-route="/interests"] .int-picker-item.is-chosen .int-picker-dim{ opacity: 0 }
.is-int-theme body[data-route="/interests"] :is(.int-picker-label, .int-picker-sub, .int-picker-keys){
  margin: 0;
  padding: 0 16px;
  text-align: center;
  color: var(--pk-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 1px 0 0 var(--pk-outline), -1px 0 0 var(--pk-outline), 0 1px 0 var(--pk-outline), 0 -1px 0 var(--pk-outline),
               1px 1px 0 var(--pk-outline), -1px -1px 0 var(--pk-outline), 1px -1px 0 var(--pk-outline), -1px 1px 0 var(--pk-outline);
}
.is-int-theme body[data-route="/interests"] .int-picker-label{
  margin-top: 16px;
  font-weight: 600;
  font-size: 24px;
  line-height: 1.3;
}
.is-int-theme body[data-route="/interests"] .int-picker-sub{
  margin-top: 8px;
  font-size: 14px;
  line-height: 1.4;
  opacity: .85;
}
.is-int-theme body[data-route="/interests"] .int-picker-sub[hidden]{ display: block; visibility: hidden }
/* The keys, faintly, for a visitor who has never seen Omarchy. Keyboard
   devices only: on a touch screen the cards are simply tapped. */
.is-int-theme body[data-route="/interests"] .int-picker-keys{
  margin-top: 18px;
  font-size: 11.5px;
  line-height: 1.4;
  letter-spacing: .02em;
  opacity: .55;
}
@media (hover: none), (pointer: coarse){
  .is-int-theme body[data-route="/interests"] .int-picker-keys{ display: none }
}
.is-int-theme body[data-route="/interests"] #int-picker.is-instant :is(.int-picker-item, .int-picker-shot, .int-picker-dim){
  transition: none;
}
@media (prefers-reduced-motion: reduce){
  .is-int-theme body[data-route="/interests"] #int-picker,
  .is-int-theme body[data-route="/interests"] #int-picker :is(.int-picker-item, .int-picker-shot, .int-picker-dim){
    transition: none;
  }
}
/* System cursors over the picker, as over the settings panel: the ids
   outrank every sword rule, the themed ones included. */
#int-picker, #int-picker *{ cursor: default }
#int-picker .int-picker-item, #int-picker .int-picker-item *{ cursor: pointer }

/* The keys, hinted where they will be found and nowhere else: a keycap in
   the Wallpaper and Themes tooltips, and a line at the foot of those two
   panes. Only where there is a keyboard to press them on. */
.is-int-theme body[data-route="/interests"] .int-kbd{
  display: inline-block;
  min-width: 1.7em;
  margin-left: 8px;
  padding: 2px 7px;
  border: 1px solid rgb(255 255 255 / .5);
  border-bottom-width: 2px;          /* a key, pressed in from above */
  border-radius: 5px;
  font: 700 13px/1.25 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
  text-align: center;
  color: inherit;
  background: rgb(255 255 255 / .14);
}
/* A tip, set apart in a soft box at the foot of the pane so it is seen
   without shouting. */
.is-int-theme body[data-route="/interests"] .int-pane-hint{
  margin: 20px 0 0;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgb(15 23 42 / .05);
  font: 400 14px/1.6 'Inter', system-ui, sans-serif;
  color: var(--int-ui-ink);
}
.is-int-theme body[data-route="/interests"] .int-pane-hint .int-kbd{
  margin: 0 3px;
  border-color: rgb(15 23 42 / .3);
  background: #fff;
  color: var(--int-ui-ink);
}
@media (hover: none), (pointer: coarse){
  .is-int-theme body[data-route="/interests"] :is(.int-kbd, .int-pane-hint){ display: none }
}

/* ============================================
   THE TIP
   A small card by the tools rail (js/interests-theme.js, "The tip"): on a
   desktop just under the rail with a point up at it, on a tablet or phone
   just above the tools pill with a point down at it. It wears the current
   theme's picker colours (--hint-bg, --hint-fg, --hint-accent, set by the
   script) and the column's typeface, keeps to the painting beside the
   column where there is room, and steps aside while the settings panel or
   the picker is open.
   ============================================ */
#int-ui .int-hint[hidden]{ display: none !important }
.is-int-theme body[data-route="/interests"] .int-hint{
  position: fixed;
  z-index: 1095;
  top: 218px;
  right: 12px;
  width: clamp(230px, calc((100vw - var(--int-col-w)) / 2 - 36px), 300px);
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px 8px 12px 14px;
  border: 1px solid color-mix(in srgb, var(--hint-fg) 16%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--hint-bg) 88%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
  backdrop-filter: blur(10px) saturate(130%);
  box-shadow: 0 10px 28px rgb(0 0 0 / .22);
  color: var(--hint-fg);
  font-family: var(--int-font, 'Inter', system-ui, sans-serif);
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0;
  translate: 0 -6px;
  transition: opacity .35s ease, translate .35s cubic-bezier(.22,1,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-hint.is-shown{
  opacity: 1;
  translate: 0;
}
/* the point, up at the rail's middle */
.is-int-theme body[data-route="/interests"] .int-hint::before{
  content: '';
  position: absolute;
  top: -6px;
  right: 6px;
  width: 10px;
  height: 10px;
  rotate: 45deg;
  background: inherit;
  border-top: 1px solid color-mix(in srgb, var(--hint-fg) 16%, transparent);
  border-left: 1px solid color-mix(in srgb, var(--hint-fg) 16%, transparent);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.is-int-theme body[data-route="/interests"] .int-hint-text{
  flex: 1 1 auto;
  margin: 0;
}
/* T and W as keys in the theme's accent; they open the pickers too */
.is-int-theme body[data-route="/interests"] .int-hint-key{
  display: inline-block;
  min-width: 1.7em;
  margin: 0 2px;
  padding: 0 6px;
  border: 1px solid color-mix(in srgb, var(--hint-accent) 70%, transparent);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: color-mix(in srgb, var(--hint-accent) 18%, transparent);
  color: var(--hint-fg);
  font: 700 12.5px/1.45 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
  text-align: center;
  vertical-align: baseline;
  cursor: pointer;
  transition: background .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-hint-key:hover{
  background: color-mix(in srgb, var(--hint-accent) 34%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-hint-icon{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin: 0 2px;
  padding: 0;
  vertical-align: middle;
  border: 1px solid color-mix(in srgb, var(--hint-accent) 70%, transparent);
  border-radius: 7px;
  background: color-mix(in srgb, var(--hint-accent) 18%, transparent);
  color: var(--hint-fg);
}
.is-int-theme body[data-route="/interests"] .int-hint-icon svg{ width: 15px; height: 15px }
.is-int-theme body[data-route="/interests"] .int-hint-close{
  flex: none;
  width: 24px;
  height: 24px;
  margin: -4px 0 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: color-mix(in srgb, var(--hint-fg) 70%, transparent);
  font: 400 17px/1 'Inter', system-ui, sans-serif;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-hint-close:hover{
  background: color-mix(in srgb, var(--hint-fg) 12%, transparent);
  color: var(--hint-fg);
}
/* The site's phone rule gives every button a 44px box (styles.css), which
   swelled these inline ones and pushed the lines of the tip apart. They keep
   their drawn size here and reach 44px with an invisible hit area. */
.is-int-theme body[data-route="/interests"] :is(.int-hint-key, .int-hint-icon, .int-hint-close){
  position: relative;
  min-width: 0;
  min-height: 0;
}
@media (pointer: coarse){
  .is-int-theme body[data-route="/interests"] :is(.int-hint-key, .int-hint-icon)::after{
    content: '';
    position: absolute;
    inset: -9px -6px;
  }
  .is-int-theme body[data-route="/interests"] .int-hint-close::after{
    content: '';
    position: absolute;
    inset: -10px;
  }
}
/* Keys where there is a keyboard, the tools' icons where there is not. */
.is-int-theme body[data-route="/interests"] .int-hint-touch{ display: none }
@media (hover: none), (pointer: coarse){
  .is-int-theme body[data-route="/interests"] .int-hint-keyboard{ display: none }
  .is-int-theme body[data-route="/interests"] .int-hint-touch{ display: inline }
}
/* Out of the way while the panel or the picker is up. */
.is-int-theme.is-panel-open body[data-route="/interests"] .int-hint,
.is-int-theme body.is-panel-open[data-route="/interests"] .int-hint,
.is-int-theme.is-picker-open body[data-route="/interests"] .int-hint{ display: none }
/* Tablets and phones: the tools are a pill in the bottom corner, so the tip
   sits just above it and points down. */
@media (max-width: 980px){
  .is-int-theme body[data-route="/interests"] .int-hint{
    top: auto;
    right: 12px;
    bottom: calc(76px + env(safe-area-inset-bottom, 0px));
    width: min(320px, calc(100vw - 24px));
    translate: 0 6px;
  }
  .is-int-theme body[data-route="/interests"] .int-hint::before{
    top: auto;
    bottom: -6px;
    right: 40px;
    border-top: 0;
    border-left: 0;
    border-right: 1px solid color-mix(in srgb, var(--hint-fg) 16%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--hint-fg) 16%, transparent);
  }
}
@media (prefers-reduced-motion: reduce){
  .is-int-theme body[data-route="/interests"] .int-hint{ transition: none; translate: 0 }
}
/* System cursors on the tip, as on the panel. */
#int-ui .int-hint, #int-ui .int-hint *{ cursor: default }
#int-ui .int-hint button, #int-ui .int-hint button *{ cursor: pointer }

/* ============================================
   THEMED CURSORS
   A theme with a cursor of its own puts its name on <html> as data-cursor
   (js/interests-theme.js, from the painting's set; the head script restores
   it for the first frame). Each swaps the Zenith's five sprites for the same
   sword recoloured in the theme's palette (tools/make-cursors.py writes
   images/cursors/), with the same hotspots and the same four states as the
   rules in styles.css: resting, over something actionable, pressed, and the
   faded blade over prose. Main only, like the rest of the theme; the
   settings panel keeps its system cursors (the id rule below outranks these).
   The trail's colours follow the same attribute (js/zenith-trail.js).
   ============================================ */
.is-int-theme[data-cursor="solitude"] body[data-route="/interests"]{
  cursor: url("./images/cursors/solitude-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="solitude"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/solitude-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/solitude-text-48.png") 1x, url("./images/cursors/solitude-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="solitude"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/solitude-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="solitude"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/solitude-press-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="kanagawa"] body[data-route="/interests"]{
  cursor: url("./images/cursors/kanagawa-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="kanagawa"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/kanagawa-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/kanagawa-text-48.png") 1x, url("./images/cursors/kanagawa-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="kanagawa"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/kanagawa-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="kanagawa"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/kanagawa-press-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="everforest"] body[data-route="/interests"]{
  cursor: url("./images/cursors/everforest-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="everforest"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/everforest-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/everforest-text-48.png") 1x, url("./images/cursors/everforest-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="everforest"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/everforest-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="everforest"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/everforest-press-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="gruvbox"] body[data-route="/interests"]{
  cursor: url("./images/cursors/gruvbox-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="gruvbox"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/gruvbox-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/gruvbox-text-48.png") 1x, url("./images/cursors/gruvbox-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="gruvbox"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/gruvbox-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="gruvbox"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/gruvbox-press-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="last-horizon"] body[data-route="/interests"]{
  cursor: url("./images/cursors/last-horizon-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="last-horizon"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/last-horizon-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/last-horizon-text-48.png") 1x, url("./images/cursors/last-horizon-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="last-horizon"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/last-horizon-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="last-horizon"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/last-horizon-press-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="osaka-jade"] body[data-route="/interests"]{
  cursor: url("./images/cursors/osaka-jade-96.png") 36 22, auto;
}
.is-int-theme[data-cursor="osaka-jade"] body[data-route="/interests"]
  :is(p, li, blockquote, cite, figcaption, dt, dd, h1, h2, h3, h4, h5, h6, td, th){
  cursor: url("./images/cursors/osaka-jade-text-48.png") 18 11, text;
  cursor: image-set(url("./images/cursors/osaka-jade-text-48.png") 1x, url("./images/cursors/osaka-jade-text-96.png") 2x) 18 11, text;
}
.is-int-theme[data-cursor="osaka-jade"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card){
  cursor: url("./images/cursors/osaka-jade-hover-96.png") 36 22, pointer;
}
.is-int-theme[data-cursor="osaka-jade"] body[data-route="/interests"]
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]), .card):active{
  cursor: url("./images/cursors/osaka-jade-press-96.png") 36 22, pointer;
}

/* The settings panel drops the Zenith sword for the system cursors. Its hotspot
   is the blade tip, but the sprite's bulk hangs down and to the right of it and
   covers the very thumbnail, swatch or slider you are aiming at. The id outranks
   every Zenith rule in styles.css, so `inherit` beats the sword (and the faded
   prose blade on the panel title) on every descendant and lets the panel's
   `auto` flow down. The last rule hands back the plain hand on anything
   clickable, and because the rest inherit, a thumbnail <img> or an icon inside
   a button keeps that hand too. zenith-trail.js stops the ribbon over the
   panel to match. */
.is-int-theme body[data-route="/interests"] #int-panel{
  cursor: auto;
}
.is-int-theme body[data-route="/interests"] #int-panel *{
  cursor: inherit;
}
.is-int-theme body[data-route="/interests"] #int-panel
  :is(a, button, summary, label, select, [role="button"], [tabindex]:not([tabindex="-1"]),
      input:is([type="checkbox"], [type="radio"], [type="color"])){
  cursor: pointer;
}


/* ============================================
   PICKER, PALETTE, TYPEFACES
   ============================================ */
.is-int-theme body[data-route="/interests"] .int-grid{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-thumb{
  aspect-ratio: 4 / 3;
  min-height: 0;
  padding: 0;
  border: 2px solid rgb(15 23 42 / .12);
  border-radius: 8px;
  overflow: hidden;
  background-color: rgb(15 23 42 / .04);
  background-size: cover;
  background-position: center;
  transition: border-color .15s ease, transform .12s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-thumb:hover{
  border-color: rgb(15 23 42 / .32);
  background-color: rgb(15 23 42 / .04);
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-thumb[aria-pressed="true"]{
  border-color: rgb(15 23 42 / .6);
  background-color: rgb(15 23 42 / .04);
}

.is-int-theme body[data-route="/interests"] .int-panel-body .int-dice{
  width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: #555;
  font-size: 16px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-dice:hover{
  background: rgb(15 23 42 / .06);
  color: #1a1a1a;
}
.is-int-theme body[data-route="/interests"] .int-dice-glyph{
  display: block;
  transition: scale .2s ease;
}
.is-int-theme body[data-route="/interests"] .int-dice:hover .int-dice-glyph{ scale: 1.1 }
.is-int-theme body[data-route="/interests"] .int-dice.is-rolling .int-dice-glyph{
  animation: int-dice-roll .6s cubic-bezier(.34,1.4,.5,1);
}
@keyframes int-dice-roll{
  0%{ rotate: 0deg; scale: 1 }
  50%{ rotate: 180deg; scale: .78 }
  100%{ rotate: 360deg; scale: 1 }
}

/* Title and credit, with a fixed height so the panel does not jump as titles
   of different lengths replace each other. */
.is-int-theme body[data-route="/interests"] .int-art-line{
  height: 40px;
  overflow: hidden;
  margin-top: 10px;
  line-height: 1.35;
  animation: int-fade-in .25s ease;
}
.is-int-theme body[data-route="/interests"] .int-art-title{
  display: block;
  font: 600 13px/1.35 'Inter', system-ui, sans-serif;
  color: #1a1a1a;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.is-int-theme body[data-route="/interests"] .int-art-credit{
  display: block;
  font: 400 12px/1.35 'Inter', system-ui, sans-serif;
  color: #6a6a6a;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@keyframes int-fade-in{ from{ opacity: 0 } to{ opacity: 1 } }

.is-int-theme body[data-route="/interests"] .int-palette{
  display: flex;
  height: 32px;
  margin: 2px 0 8px;
  border: 1px solid rgb(15 23 42 / .08);
  border-radius: 6px;
  overflow: visible;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-sw{
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-sw:hover{ background: inherit }
.is-int-theme body[data-route="/interests"] .int-palette .int-sw:first-child{ border-radius: 5px 0 0 5px }
.is-int-theme body[data-route="/interests"] .int-palette .int-sw:last-child{ border-radius: 0 5px 5px 0 }
/* The reference clips this tooltip against its own scroll container on the
   first and last swatch; clamping it to the row keeps every one readable. */
.is-int-theme body[data-route="/interests"] .int-sw-tip{
  position: absolute;
  bottom: calc(100% + 7px);
  left: 50%;
  translate: -50% 0;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgb(28 28 30 / .92);
  color: #fff;
  font: 400 11px/1.3 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  z-index: 2;
}
.is-int-theme body[data-route="/interests"] .int-sw:is(:hover, :focus-visible) .int-sw-tip{ opacity: 1 }
.is-int-theme body[data-route="/interests"] .int-palette .int-sw:first-child .int-sw-tip{
  left: 0;
  translate: 0 0;
}
.is-int-theme body[data-route="/interests"] .int-palette .int-sw:last-child .int-sw-tip{
  left: auto;
  right: 0;
  translate: 0 0;
}

.is-int-theme body[data-route="/interests"] .int-faces{
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face{
  justify-content: space-between;
  min-height: 28px;
  /* No side bleed on these rows. The panel body scrolls vertically, which
     computes overflow-x to auto as well, so a row hanging into the panel
     padding would be clipped and would put a scrollbar under the list. */
  margin: 0;
  padding: 5px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.3;
  text-align: left;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face:hover{
  background: rgb(15 23 42 / .06);
  border-color: transparent;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face:active{
  background: rgb(15 23 42 / .16);
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face[aria-pressed="true"]{
  background: transparent;
  border-color: transparent;
}
.is-int-theme body[data-route="/interests"] .int-face-check{
  font-size: 13px;
  color: #666;
  opacity: 0;
}
.is-int-theme body[data-route="/interests"] .int-face[aria-pressed="true"] .int-face-check{ opacity: 1 }

.is-int-theme body[data-route="/interests"] .int-panel-body .int-copy,
.is-int-theme body[data-route="/interests"] .int-panel-foot .int-copy{
  width: 100%;
  min-height: 32px;
}
.is-int-theme body[data-route="/interests"] .int-panel-foot .int-copy{
  border: 1px solid var(--int-ui-line);
  border-radius: 8px;
  background: rgb(255 255 255 / .5);
  color: #444;
  font: 600 13px/1.2 'Inter', system-ui, sans-serif;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: background .15s ease, color .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-foot .int-copy.is-copied,
.is-int-theme body[data-route="/interests"] .int-panel-body .int-copy.is-copied{
  background: #1a1a1a;
  border-color: #1a1a1a;
  color: #fff;
}


/* ============================================
   INTERESTS THEME - responsive
   Desktop first, matching the rest of the file this one loads after.
   ============================================ */

/* 981-1259px: the open settings panel (300px, 48px in from the right) would
   cover the ends of the lines, so the text steps left while it is open. */
@media (min-width: 981px) and (max-width: 1259px){
  .is-int-theme.is-panel-open body[data-route="/interests"] .int-main{
    margin-left: auto;
    margin-right: 332px;
  }
}

@media (max-width: 980px){
  /* The column is now the whole width of the page, so the text goes back to
     being centred in it with no mirrored padding. */
  .is-int-theme body[data-route="/interests"] .int-main{
    max-width: var(--int-measure);
    padding-right: 0;
    margin-inline: auto;
  }

  /* The rail becomes a compact floating pill. The DOM contract gives us three
     buttons in one <nav>, and there is no markup for an "Appearance" button
     inside the index bar, so the same three buttons dock to the corner
     instead: same control, same order, no extra element. */
  .is-int-theme body[data-route="/interests"] .int-tools{
    top: auto;
    right: 16px;
    bottom: 16px;
    width: auto;
    flex-direction: row;
    gap: 4px;
    padding: 4px;
    border: 1px solid var(--int-ui-line);
    border-radius: 999px;
    box-shadow: 0 8px 24px rgb(0 0 0 / .22);
  }
  .is-int-theme body[data-route="/interests"] .int-tool-tip{
    top: auto;
    right: auto;
    bottom: calc(100% + 10px);
    left: 50%;
    translate: -50% 4px;
  }
  .is-int-theme body[data-route="/interests"] .int-tool:is(:hover, :focus-visible) .int-tool-tip{
    translate: -50% 0;
  }

  /* The panel becomes a bottom sheet, sitting directly above the pill so that
     the pane switcher stays reachable while the sheet is open. */
  .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
    top: auto;
    right: 12px;
    left: 12px;
    bottom: 76px;
    width: auto;
    max-height: 70vh;
    padding: 8px 18px 16px;
    border: 1px solid var(--int-ui-line);
    border-radius: 18px;
    background: var(--int-ui-glass-solid);
    box-shadow: 0 16px 40px rgb(0 0 0 / .28);
    z-index: 1100;
    translate: 0;
  }
  @starting-style{
    .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
      opacity: 0;
      translate: 0 12px;
    }
  }
  /* Drag handle. Decorative only - the sheet closes on Esc, on the backdrop
     and on the same trigger again, all of which are script. */
  .is-int-theme body[data-route="/interests"] .int-panel-head::before{
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    translate: -50% 0;
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: rgb(15 23 42 / .18);
  }
  .is-int-theme body[data-route="/interests"] .int-panel-head{
    position: relative;
    padding-top: 16px;
  }

  /* A scrim behind the sheet. The markup's own .int-panel-backdrop is used
     when it is there, because a real element can take the tap that closes the
     sheet; the pseudo-element is the fallback for when it is not, and the
     :not(:has()) guard keeps the two from ever stacking up as one double-dark
     scrim. */
  .is-int-theme body[data-route="/interests"] .int-panel-backdrop:not([hidden]){
    display: block;
    position: fixed;
    inset: 0;
    z-index: 1089;
    background: rgb(0 0 0 / .28);
  }
  .is-int-theme body[data-route="/interests"]
    #int-ui:not(:has(.int-panel-backdrop)):has(.int-panel:not([hidden]))::before{
    content: '';
    position: fixed;
    inset: 0;
    z-index: 1089;
    background: rgb(0 0 0 / .28);
    pointer-events: auto;
  }
}

/* ---- 769-980 ----
   The index is a bar across the top here (styles.css, "Site navigation"),
   12-58px, so a jump lands a section's head just under it. */
@media (min-width: 769px) and (max-width: 980px){
  .is-int-theme body[data-route="/interests"] .int-group{ scroll-margin-top: 80px }

  /* The settings panel: a narrow sheet at the right instead of the phone
     sheet stretched to the full width, with no dark scrim over the painting
     and column it is adjusting. A tap outside still closes it. */
  .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
    left: auto;
    right: 12px;
    width: min(380px, calc(100vw - 24px));
    max-height: min(70vh, 640px);
  }
  .is-int-theme body[data-route="/interests"] .int-panel-head::before{ display: none }
  .is-int-theme body[data-route="/interests"] .int-panel-backdrop:not([hidden]){ background: transparent }
  .is-int-theme body[data-route="/interests"]
    #int-ui:not(:has(.int-panel-backdrop)):has(.int-panel:not([hidden]))::before{ background: transparent }
}
/* Squat tablet-width screens (squares, landscape windows): the column's
   82vh start put the page title below the fold, so it starts higher. Scaled,
   not capped, so the Start slider still moves it. */
@media (min-width: 769px) and (max-width: 980px) and (min-aspect-ratio: 4/5){
  .is-int-theme body[data-route="/interests"] .int-sheet{ --int-col-top: calc(var(--int-col-start) * .72) }
}

/* ---- the end of the page on phones ----
   Past the reading column the page goes on for a while over the bare
   painting, so the last screen is mostly the painting: scrolled to the very
   end, the column ends a little over a third of the way down. The copyright
   sits at the foot of that space, above the appearance button in the corner
   rather than under it. The column itself ends as it begins, 32px past its
   text: its end is now in the middle of that last view, and the deeper tail
   it used to keep (clearance for the old row of icons under it) read as an
   empty box, the whole of the column left on screen on a phone on its side.
   Phones held either way; tablets and desktops keep the page as it was. */
@media (max-width: 768px), (max-width: 980px) and (max-height: 500px) and (pointer: coarse){
  .is-int-theme body[data-route="/interests"] .int-sheet{ padding-bottom: 32px }
  .is-int-theme body[data-route="/interests"] .footer{
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 62vh;
    min-height: 62svh;
    padding: 24px 24px calc(80px + env(safe-area-inset-bottom, 0px));
  }
  .is-int-theme body[data-route="/interests"] .footer p{ margin: 0 }
}

/* ---- phones ---- */
@media (max-width: 768px){
  /* The fill runs edge to edge here, so the text takes a 20px gutter of its
     own. The index bar below is fixed to the viewport and repeats the same
     20px as its own padding, so its labels line up with the text. */
  .is-int-theme body[data-route="/interests"] .int-shell{ padding-inline: 20px }

  /* At 82vh the column began behind the dock, so a phone opened on nothing
     but the painting, with no hint there was anything below it. Scaling the
     Start slider to 60% of its value puts the default at about half the
     screen: the title and the first section show under the painting, and
     that says "scroll". Scaled rather than capped so the whole slider still
     moves the column, and applied here rather than as a phone default in the
     script because a stored 82 from before would have kept the empty screen. */
  .is-int-theme body[data-route="/interests"] .int-sheet{
    --int-col-top: calc(var(--int-col-start) * .6);
  }

  /* Both the pill and the sheet clear the home indicator. */
  .is-int-theme body[data-route="/interests"] .int-tools{
    right: 12px;
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  }
  /* styles.css gives every button a 44px minimum on phones; the panel's own
     rules are more specific and would undo it, so the touch targets are
     restored here deliberately rather than by accident. */
  .is-int-theme body[data-route="/interests"] .int-tool{ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel-body button{ min-height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-sw{ min-height: 34px }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-dice{ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
    right: 0;
    left: 0;
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    /* short screens (square phones, landscape): the top edge and the close
       button stay on screen */
    max-height: min(70vh, calc(100vh - 84px - env(safe-area-inset-bottom, 0px)));
    max-height: min(70vh, calc(100dvh - 84px - env(safe-area-inset-bottom, 0px)));
    border-radius: 18px 18px 0 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
  }

  /* Phone sizes, restated in em so the Size slider still owns them. */
  .is-int-theme body[data-route="/interests"] .int-group-title{
    font-size: calc(1.4em * var(--int-head-scale));
  }
  .is-int-theme body[data-route="/interests"] .int-name{ font-size: 1.15em }
  .is-int-theme body[data-route="/interests"] .int-meta{ font-size: .64em }
  .is-int-theme body[data-route="/interests"] .int-note{ font-size: .9em }
  .is-int-theme body[data-route="/interests"] .int-excerpt p{ font-size: 1.12em }
  .is-int-theme body[data-route="/interests"] #int-poetry .int-excerpt p{ font-size: 1.15em }
  .is-int-theme body[data-route="/interests"] .int-quote blockquote p{ font-size: 1.2em }
  .is-int-theme body[data-route="/interests"] .book-quote{ padding: 18px 20px }
  .is-int-theme body[data-route="/interests"] .book-quote p{ font-size: 1.1em }
  .is-int-theme body[data-route="/interests"] .book-title{ font-size: 1.3em }
  .is-int-theme body[data-route="/interests"] .int-intro-name{ font-size: calc(1.625em * var(--int-head-scale)) }
  .is-int-theme body[data-route="/interests"] .int-intro-tagline{ font-size: 1em }
  .is-int-theme body[data-route="/interests"] .int-intro{ margin-bottom: 44px }
  .is-int-theme body[data-route="/interests"] .xp-role{ font-size: 1.15em }
  .is-int-theme body[data-route="/interests"] .xp-desc{ font-size: .9em }
}

@media (max-width: 480px){
  .is-int-theme body[data-route="/interests"] .int-grid{ grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .is-int-theme body[data-route="/interests"] .int-intro{ --intro-ground: .875em }
  .is-int-theme body[data-route="/interests"] .int-intro-tree img{ height: 6.4375em }
}


/* ============================================
   PANEL CONTROLS
   The parts the script actually renders. The rules above were written against
   a different set of names (.int-grid, .int-thumb, .int-sw, .int-art-line), so
   without these the picker thumbnails came out at full panel width and the
   switches, palette and typeface rows fell back to the generic button look.
   Same selector prefix as the rest of the file, so source order decides.
   ============================================ */

/* The rail is only as tall as its three buttons. Running it to the bottom of
   the viewport drew a pale empty strip down the whole right edge of the page.
   This has to sit after the rail's own rule above, not with the tick colours,
   or it loses on source order at equal specificity.
   Desktop only. Unscoped, its bottom:auto also landed on the floating pill of
   the responsive block, which sets top:auto - with neither edge set, a fixed
   box falls to its static position, and #int-ui sits after <main>, so on every
   screen up to 980px the pill (and with it every appearance setting) hung
   about 19,000px down the page, out of reach. */
@media (min-width: 981px){
  .is-int-theme body[data-route="/interests"] .int-tools{
    bottom: auto;
    padding: 6px;
    border: 1px solid var(--int-ui-line);
    border-right: 0;
    border-radius: 12px 0 0 12px;
  }
}

/* Section headings inside a pane. */
.is-int-theme body[data-route="/interests"] .int-pane-head{
  display: block;
  margin: 24px 0 10px;
  font: 600 13px/1.2 'Inter', system-ui, sans-serif;
  letter-spacing: .02em;
  color: #111827;
}
.is-int-theme body[data-route="/interests"] .int-pane > .int-pane-head:first-child{ margin-top: 0 }

/* The slider fill: the script names it --int-fill, the rule above expects
   --int-ctl-p. Accept either rather than renaming across two files. */
.is-int-theme body[data-route="/interests"] .int-ctl-fill{
  width: var(--int-fill, var(--int-ctl-p, 0%));
}

/* ---- themes ----
   One card per theme: a picture of the look across the top (the painting
   under the theme's darkening, its column in its own fill and blend, a few
   lines in its typeface), the name and a line about it beneath, and a tick
   beside the name on the card whose settings the page is showing. */
.is-int-theme body[data-route="/interests"] .int-themes{
  display: grid;
  gap: 8px;
  margin-top: 10px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-theme{
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20px;
  grid-template-areas: "shot shot" "body tick";
  align-items: start;
  gap: 10px 8px;
  min-height: 0;
  padding: 8px;
  border: 2px solid rgb(15 23 42 / .12);
  border-radius: 10px;
  background: rgb(15 23 42 / .04);
  text-align: left;
  color: var(--int-ui-ink);
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-theme:hover{ background: rgb(15 23 42 / .07) }
/* The chosen card wears the same ring as the chosen painting. Written to
   outweigh the panel's generic pressed-button rule above, which would
   otherwise put its grey border on the card. */
.is-int-theme body[data-route="/interests"] .int-panel-body button.int-theme[aria-pressed="true"]{
  border-color: #2f6a85;
  box-shadow: 0 0 0 3px rgb(47 106 133 / .3);
  background: rgb(15 23 42 / .04);
}
.is-int-theme body[data-route="/interests"] .int-theme-shot{
  grid-area: shot;
  position: relative;
  display: block;
  aspect-ratio: 16 / 7;
  border-radius: 6px;
  overflow: hidden;
  background-position: center;
  background-size: cover;
  isolation: isolate;                /* the column's blend stays inside the shot */
}
.is-int-theme body[data-route="/interests"] .int-theme-dark{
  position: absolute;
  inset: 0;
  background: #000;
}
.is-int-theme body[data-route="/interests"] .int-theme-col{
  position: absolute;
  top: 30%;
  bottom: 0;
  left: 30%;
  right: 30%;
  border-radius: 2px 2px 0 0;
}
.is-int-theme body[data-route="/interests"] .int-theme-lines{
  position: absolute;
  top: 30%;
  left: 30%;
  right: 30%;
  bottom: 0;
  padding: 6px 8px 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  line-height: 1;
}
.is-int-theme body[data-route="/interests"] .int-theme-lines i{
  display: block;
  font-style: normal;
}
.is-int-theme body[data-route="/interests"] .int-theme-lines i:empty{
  height: 2px;
  width: 78%;
  border-radius: 1px;
  background: currentColor;
  opacity: .55;
}
.is-int-theme body[data-route="/interests"] .int-theme-lines i:empty + i:empty{ width: 56% }
.is-int-theme body[data-route="/interests"] .int-theme-body{
  grid-area: body;
  display: grid;
  gap: 3px;
  min-width: 0;
}
.is-int-theme body[data-route="/interests"] .int-theme-name{
  font: 600 13px/1.25 'Inter', system-ui, sans-serif;
  color: #111827;
}
.is-int-theme body[data-route="/interests"] .int-theme-blurb{
  font: 400 11.5px/1.4 'Inter', system-ui, sans-serif;
  color: var(--int-ui-ink-soft);
}
.is-int-theme body[data-route="/interests"] .int-theme-tick{
  grid-area: tick;
  justify-self: end;
  font-size: 14px;
  line-height: 1.25;   /* level with the name's first line */
  color: #2f6a85;
  opacity: 0;
  transition: opacity .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-theme[aria-pressed="true"] .int-theme-tick{ opacity: 1 }

/* ---- painting picker ----
   Every wallpaper is in the grid, but only the current theme's set is shown:
   the script hides the rest. The generic button rule above would otherwise
   give a hidden one display:inline-flex and show it anyway. */
.is-int-theme body[data-route="/interests"] .int-panel-body .int-pick-item[hidden]{ display: none }
.is-int-theme body[data-route="/interests"] .int-panel-body .int-dice[hidden]{ display: none }
.is-int-theme body[data-route="/interests"] .int-pick-head{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.is-int-theme body[data-route="/interests"] .int-pick{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-pick-item{
  min-height: 0;
  padding: 0;
  border: 2px solid rgb(15 23 42 / .12);
  border-radius: 8px;
  overflow: hidden;
  background: rgb(15 23 42 / .04);
  aspect-ratio: 4 / 3;
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* The picked painting wears a ring of sky blue sampled from Mount Corcoran -
   the bluest sky of the four - with a soft halo of the same, so it stands out
   from its neighbours at a glance rather than by a slightly darker grey edge. */
.is-int-theme body[data-route="/interests"] .int-panel-body .int-pick-item[aria-pressed="true"]{
  border-color: #2f6a85;
  box-shadow: 0 0 0 3px rgb(47 106 133 / .3);
  background: rgb(15 23 42 / .04);
}
.is-int-theme body[data-route="/interests"] .int-pick-thumb{
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* While a picked painting downloads (js adds .is-loading), a light sweeps
   along a thin track at the foot of its thumbnail. It is held back 150ms, so a
   painting that is already cached never flashes it, and it leaves at once. */
.is-int-theme body[data-route="/interests"] .int-panel-body .int-pick-item{ position: relative }
.is-int-theme body[data-route="/interests"] .int-pick-item::after{
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background:
    linear-gradient(90deg, rgb(255 255 255 / 0), rgb(255 255 255 / .95), rgb(255 255 255 / 0)) no-repeat,
    rgb(15 23 42 / .45);
  background-size: 40% 100%, 100% 100%;
  background-position: -67% 0, 0 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease;
}
.is-int-theme body[data-route="/interests"] .int-pick-item.is-loading::after{
  opacity: 1;
  transition: opacity .2s ease .15s;
  animation: int-pick-load 1.1s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes int-pick-load{
  from{ background-position: -67% 0, 0 0 }
  to  { background-position: 167% 0, 0 0 }
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-dice{
  width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font-size: 15px;
  line-height: 1;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-dice:hover{
  background: rgb(15 23 42 / .07);
}

/* Title and credit of the current painting. */
.is-int-theme body[data-route="/interests"] .int-art-meta{
  min-height: 38px;
  margin-top: 10px;
  opacity: 0;
  transition: opacity .25s ease;
}
.is-int-theme body[data-route="/interests"] .int-art-meta.is-in{ opacity: 1 }
.is-int-theme body[data-route="/interests"] .int-art-title{
  display: block;
  font: 600 13px/1.35 'Inter', system-ui, sans-serif;
  color: #111827;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.is-int-theme body[data-route="/interests"] .int-art-credit{
  display: block;
  font: 400 12px/1.35 'Inter', system-ui, sans-serif;
  color: #6a6a6a;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- palette strip ---- */
.is-int-theme body[data-route="/interests"] .int-palette{
  display: flex;
  height: 32px;
  margin-top: 4px;
  border: 1px solid var(--int-ui-line);
  border-radius: 6px;
  overflow: visible;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-swatch{
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-swatch:first-child{ border-radius: 5px 0 0 5px }
.is-int-theme body[data-route="/interests"] .int-panel-body .int-swatch:last-child{ border-radius: 0 5px 5px 0 }
.is-int-theme body[data-route="/interests"] .int-swatch-tip{
  position: absolute;
  bottom: calc(100% + 7px);
  left: 50%;
  translate: -50% 0;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgb(28 28 30 / .92);
  color: #fff;
  font: 400 11px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
}
/* The end swatches would push their tooltip past the panel, which clips it. */
.is-int-theme body[data-route="/interests"] .int-swatch[data-edge="start"] .int-swatch-tip{
  left: 0;
  translate: 0 0;
}
.is-int-theme body[data-route="/interests"] .int-swatch[data-edge="end"] .int-swatch-tip{
  left: auto;
  right: 0;
  translate: 0 0;
}
.is-int-theme body[data-route="/interests"] .int-swatch:is(:hover, :focus-visible) .int-swatch-tip{ opacity: 1 }

/* ---- switch ----
   The row carries role="switch"; the track and knob are its own spans. The
   generic [role="switch"] rules above leave these rows alone: aimed at the
   whole row, they squeezed it into a bare 44px toggle with its label ("Show
   reading column", "Paper texture") at zero width. */
.is-int-theme body[data-route="/interests"] .int-ctl--switch{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 32px;
  cursor: pointer;
}
.is-int-theme body[data-route="/interests"] .int-switch{
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 26px;
  border-radius: 13px;
  background: rgb(15 23 42 / .18);
  transition: background-color .2s cubic-bezier(.22,1,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-switch-knob{
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgb(0 0 0 / .25);
  transition: translate .2s cubic-bezier(.22,1,.36,1);
}
.is-int-theme body[data-route="/interests"] .int-ctl--switch[aria-checked="true"] .int-switch{
  background: rgb(var(--int-accent-rgb, 37 99 235) / .75);
}
.is-int-theme body[data-route="/interests"] .int-ctl--switch[aria-checked="true"] .int-switch-knob{
  translate: 18px 0;
}

/* ---- segmented groups, presets, style buttons ---- */
.is-int-theme body[data-route="/interests"] .int-seg{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}

/* ---- preview tiles: Blend, Background effect, Column effect ----
   Each option shows the current painting as it would look. Blends are drawn
   live here from the painting's thumbnail and the tint, so they follow the
   tint colour as it changes; effects are stills made once per painting
   (tools/make-previews.py), and Water moves. The wallpaper's Grayscale,
   Darken and Tint, and the column's texture, Darken and Grayscale, are laid
   over them as the page lays them. The tile carries the ring, as the
   painting picker's tiles do, and the name sits under it. */
.is-int-theme body[data-route="/interests"] .int-seg--tiles{
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px 8px;
}
.is-int-theme body[data-route="/interests"] .int-seg--colblend{ grid-template-columns: repeat(2, minmax(0, 1fr)) }
/* Position: Left, Centre, Right on one row, in the order they sit on screen. */
.is-int-theme body[data-route="/interests"] [data-key="column.align"] .int-seg{ grid-template-columns: repeat(3, minmax(0, 1fr)) }
.is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles .int-seg-btn{
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 5px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  /* the names sit straight on the glass, over any painting: this ink keeps
     them above 4.5:1 even at Darken 100% */
  color: #111827;
  font: 500 12px/1.25 'Inter', system-ui, sans-serif;
  text-align: center;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles .int-seg-btn:hover{ background: none; color: #111827 }
.is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles .int-seg-btn:is([aria-checked="true"], .is-on){
  background: none;
  color: #111827;
  font-weight: 600;
}
.is-int-theme body[data-route="/interests"] .int-tile{
  position: relative;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  isolation: isolate;                 /* each tile's blends stay inside it */
  border: 2px solid rgb(15 23 42 / .12);
  border-radius: 8px;
  background: rgb(15 23 42 / .06);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.is-int-theme body[data-route="/interests"] .int-seg-btn:hover .int-tile{ border-color: rgb(15 23 42 / .32) }
.is-int-theme body[data-route="/interests"] .int-seg-btn:is([aria-checked="true"], .is-on) .int-tile{
  border-color: #2f6a85;
  box-shadow: 0 0 0 3px rgb(47 106 133 / .3);
}
.is-int-theme body[data-route="/interests"] :is(.int-tile, .int-tile-sheet) > *{
  position: absolute;
  inset: 0;
}
.is-int-theme body[data-route="/interests"] .int-tile-art{
  background: var(--int-thumb-url) center / cover no-repeat;
}
.is-int-theme body[data-route="/interests"] .int-tile-shot{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.is-int-theme body[data-route="/interests"] .int-tile-shot[hidden]{ display: none }
/* The wallpaper's own finish, as the page draws it over the painting. */
.is-int-theme body[data-route="/interests"] :is(.int-tile-art, .int-tile--fx .int-tile-shot, .int-tile--fx .int-tile-live){
  filter: grayscale(var(--int-wall-gray, 0%));
}
.is-int-theme body[data-route="/interests"] .int-tile-dark{ background: rgb(0 0 0 / var(--int-wall-darken, 0%)) }
.is-int-theme body[data-route="/interests"] .int-tile-wash{
  background: var(--int-tint);
  opacity: var(--int-tint-a, 0%);
  mix-blend-mode: var(--int-blend, multiply);
}
/* A blend is only visible through some tint. The tiles draw the tint at its
   own opacity, and while that is 0% at the strength a pick will bring it up
   to (js/interests-theme.js, revealTint), so each tile is what the page will
   look like. */
.is-int-theme body[data-route="/interests"] .int-tile-tint{
  background: var(--int-tint);
  opacity: var(--int-tint-tile, 50%);
}
/* Water's moving tile (js/interests-theme.js, syncLiveWater): the effect's
   canvas over its still. */
.is-int-theme body[data-route="/interests"] .int-tile-live{ pointer-events: none }
/* The column tile: the painting either side and the column down the middle,
   blurred behind its fill as the page's is, the effect multiplied into the
   fill (the page's own opacities, set by the script) and a few lines of text
   in the text colour. The effect still is as wide as the whole tile and
   pulled left to line up with the painting around it. */
.is-int-theme body[data-route="/interests"] .int-tile-sheet{
  left: 24%;
  right: 24%;
  overflow: hidden;
  isolation: isolate;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.is-int-theme body[data-route="/interests"] .int-tile-fill{
  background: var(--int-col-fill);
  background: color-mix(in srgb, var(--int-col-fill) var(--int-col-a), transparent);
}
/* The column's Blend in a tile, as on the page: the fill's colour on a layer
   under the tile's glass, mixed into the painting. The Blend tiles each carry
   their own mode; the Column effect tiles follow the chosen one. */
.is-int-theme body[data-route="/interests"] .int-tile-blend{
  left: 24%;
  right: 24%;
  background: var(--int-col-fill);
  background: color-mix(in srgb, var(--int-col-fill) var(--int-col-a), transparent);
  mix-blend-mode: var(--int-col-blend, normal);
}
.is-int-theme body[data-route="/interests"] .int-tile--col .int-tile-blend{ display: none }
.is-int-theme.is-col-blended body[data-route="/interests"] .int-tile--col .int-tile-blend,
.is-int-theme body.is-col-blended[data-route="/interests"] .int-tile--col .int-tile-blend{ display: block }
.is-int-theme.is-col-blended body[data-route="/interests"] .int-tile--col .int-tile-fill,
.is-int-theme body.is-col-blended[data-route="/interests"] .int-tile--col .int-tile-fill{ display: none }
/* the Paper texture switch, as the page's column draws it */
.is-int-theme body[data-route="/interests"] .int-tile-grain{
  background-color: #f3e8d6;
  background-image: var(--int-grain);
  background-size: 90px 90px;
  mix-blend-mode: multiply;
  opacity: var(--int-col-texture, 0);
}
/* and the column's Darken and Grayscale, on everything the column shows */
.is-int-theme.is-col-filtered body[data-route="/interests"] .int-tile-sheet,
.is-int-theme body.is-col-filtered[data-route="/interests"] .int-tile-sheet{
  filter: grayscale(var(--int-col-gray)) brightness(calc(100% - var(--int-col-darken)));
}
.is-int-theme body[data-route="/interests"] .int-tile-sheet :is(.int-tile-shot, .int-tile-live){
  right: auto;
  left: calc(-24% / .52);
  width: calc(100% / .52);
  max-width: none;                    /* styles.css caps every img at its box */
  mix-blend-mode: multiply;
}
/* Both multiplied into the fill would double it, so once the moving one has
   drawn, the still steps aside. */
.is-int-theme body[data-route="/interests"] .int-tile.is-live .int-tile-sheet .int-tile-shot{ visibility: hidden }
.is-int-theme body[data-route="/interests"] .int-tile-sheet .int-tile-lines{
  inset: 20% 13% auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.is-int-theme body[data-route="/interests"] .int-tile-lines i{
  display: block;
  height: 2px;
  border-radius: 1px;
  background: color-mix(in srgb, var(--int-tile-ink, var(--int-ink)) 55%, transparent);
}
.is-int-theme body[data-route="/interests"] .int-tile-lines i:nth-child(1){ width: 58%; height: 3px; margin-bottom: 2px; background: var(--int-tile-ink, var(--int-ink)) }
.is-int-theme body[data-route="/interests"] .int-tile-lines i:nth-child(2){ width: 94% }
.is-int-theme body[data-route="/interests"] .int-tile-lines i:nth-child(3){ width: 82% }
.is-int-theme body[data-route="/interests"] .int-tile-lines i:nth-child(4){ width: 88% }
/* Short sheets (a phone on its side, a desktop zoomed to 200%): tiles that
   follow the column width grew taller than the scroll area, so no preview
   could be seen with its name. There they take a fixed, smaller size and as
   many to a row as fit. */
@media (max-height: 500px){
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles{ grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)) }
}
.is-int-theme body[data-route="/interests"] .int-presets{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-preset{
  justify-content: flex-start;
  gap: 8px;
}
.is-int-theme body[data-route="/interests"] .int-preset-chip{
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 1px solid var(--int-ui-line);
  border-radius: 6px;
}
.is-int-theme body[data-route="/interests"] .int-style-row{
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

/* ---- colour row ---- */
.is-int-theme body[data-route="/interests"] .int-ctl--color{
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
}
.is-int-theme body[data-route="/interests"] .int-ctl--color .int-ctl-label{
  flex: 0 0 auto;
  font: 400 12px/1 'Inter', system-ui, sans-serif;
  color: #444;
}
.is-int-theme body[data-route="/interests"] .int-color-swatch{
  position: relative;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border: 1px solid var(--int-ui-line);
  border-radius: 7px;
  overflow: hidden;
}
/* The native picker sits invisibly on top of the swatch, so the swatch itself
   is the control and the browser still supplies the colour dialog. */
.is-int-theme body[data-route="/interests"] .int-color-native{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
}
.is-int-theme body[data-route="/interests"] .int-color-hex{
  flex: 1 1 auto;
  min-width: 0;
  height: 30px;
  padding: 0 10px;
  border: 1px solid rgb(15 23 42 / .15);
  border-radius: 7px;
  background: rgb(255 255 255 / .7);
  color: #444;
  font: 400 13px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.is-int-theme body[data-route="/interests"] .int-color-hex:focus-visible{
  border-color: rgb(15 23 42 / .4);
}

/* ---- typeface list ---- */
.is-int-theme body[data-route="/interests"] .int-faces{
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face{
  justify-content: space-between;
  min-height: 30px;
  padding: 5px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.3;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face:hover{
  background: rgb(15 23 42 / .06);
  border-color: transparent;
}
.is-int-theme body[data-route="/interests"] .int-panel-body .int-face[aria-checked="true"]{
  background: transparent;
  border-color: transparent;
}
.is-int-theme body[data-route="/interests"] .int-face-name{
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
}
.is-int-theme body[data-route="/interests"] .int-face-tick{
  flex: 0 0 auto;
  color: #666;
  font-size: 13px;
  opacity: 0;
}
.is-int-theme body[data-route="/interests"] .int-face[aria-checked="true"] .int-face-tick{ opacity: 1 }

/* ---- the skip button, visible only once it has focus ---- */
.is-int-theme body[data-route="/interests"] .int-skip{
  position: fixed;
  top: 8px;
  right: 56px;
  z-index: 1101;
  padding: 8px 14px;
  border: 1px solid var(--int-ui-line);
  border-radius: 999px;
  background: #111827;
  color: #fff;
  font: 600 13px/1 'Inter', system-ui, sans-serif;
  opacity: 0;
  pointer-events: none;
  translate: 0 -8px;
}
.is-int-theme body[data-route="/interests"] .int-skip:focus-visible{
  opacity: 1;
  pointer-events: auto;
  translate: 0;
}


/* ============================================
   OLDER ENGINES
   Browsers without color-mix() (Chrome and Edge before 111, Safari before
   16.2 - every iPhone left on iOS 15) cannot fall back to the plain colour
   written before a color-mix() line: a declaration holding var() is only
   checked once the variables are known, and then it computes to transparent
   rather than to the line before it. The column, its blend layer, the tip
   card and the picker's scrim went see-through there, with the text over the
   bare painting. These rules restate the plain fills, later and with the
   same selectors, and only those engines read them. Browsers without the
   individual translate property (Chromium before 104) get the switch knobs
   moved with transform instead.
   ============================================ */
@supports not (color: color-mix(in srgb, red 50%, red)){
  .is-int-theme body[data-route="/interests"] .int-sheet-fill,
  .is-int-theme body[data-route="/interests"] .int-tile-fill,
  .is-int-theme body[data-route="/interests"] .int-tile-blend{ background: var(--int-col-fill) }
  @media not all and (forced-colors: active){
    .is-int-theme.is-col-blended body[data-route="/interests"] .int-sheet-blend,
    .is-int-theme body.is-col-blended[data-route="/interests"] .int-sheet-blend{ background: var(--int-col-fill) }
    .is-int-theme.is-col-blended body[data-route="/interests"] .int-sheet-fill,
    .is-int-theme body.is-col-blended[data-route="/interests"] .int-sheet-fill{ background: transparent }
  }
  .is-int-theme body[data-route="/interests"] .int-hint{ background: var(--hint-bg) }
  .is-int-theme body[data-route="/interests"] #int-picker{ background: rgb(0 0 0 / .5) }
}
@supports not (translate: 0){
  .is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)::after{
    transition: transform .2s cubic-bezier(.22,1,.36,1);
  }
  .is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)[aria-checked="true"]::after{
    transform: translateX(18px);
  }
  .is-int-theme body[data-route="/interests"] .int-switch-knob{
    transition: transform .2s cubic-bezier(.22,1,.36,1);
  }
  .is-int-theme body[data-route="/interests"] .int-ctl--switch[aria-checked="true"] .int-switch-knob{
    transform: translateX(18px);
  }
}

/* ============================================
   REDUCED MOTION
   The global rule in styles.css already shortens every transition to 0.01ms,
   which covers the panel slide and the label reveal. What it cannot do is
   stop a layer entering at blur(18px) and staying there for a frame, so the
   crossfade is disarmed at the source instead.
   ============================================ */
@media (prefers-reduced-motion: reduce){
  .is-int-theme body[data-route="/interests"] .int-wall-img{ transition: none }
  .is-int-theme body[data-route="/interests"] .int-wall-img.is-enter{
    opacity: 1;
    filter: none;
  }
  .is-int-theme body[data-route="/interests"] .int-dice.is-rolling .int-dice-glyph{ animation: none }
  .is-int-theme body[data-route="/interests"] .int-dice:hover .int-dice-glyph{ scale: 1 }
  .is-int-theme body[data-route="/interests"] .int-art-line{ animation: none }
  /* the loading track stays, standing still */
  .is-int-theme body[data-route="/interests"] .int-pick-item.is-loading::after{
    animation: none;
    background-position: 50% 0, 0 0;
  }
  .is-int-theme body[data-route="/interests"] .int-panel:not([hidden]){
    transition: none;
    translate: 0;
  }
}


/* ============================================
   FORCED COLOURS
   A painting cannot be made legible in a forced palette, so it is removed
   rather than approximated: the column falls back to Canvas, the ink to
   CanvasText, and the panel chrome to the system button colours. Removing
   the text shadows matters as much as the colours - they are drawn in a
   colour the palette does not control.
   ============================================ */
@media (forced-colors: active){
  .is-int-theme body[data-route="/interests"] #int-stage{ display: none }
  .is-int-theme body[data-route="/interests"] .int-sheet-fill{
    background: Canvas;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    -webkit-mask-image: none;
    mask-image: none;
  }
  .is-int-theme body[data-route="/interests"] .int-sheet-fill::before{ opacity: 0 }
  .is-int-theme body[data-route="/interests"] .int-sheet{ --int-ink: CanvasText }
  .is-int-theme body[data-route="/interests"] .int-main{
    color: CanvasText;
    text-shadow: none;
  }
  /* The distress mask is drawn from an image, which forced colours leave
     alone, so it would punch holes in system-coloured text. */
  .is-int-theme body[data-route="/interests"]
    :is(.interests-head, .int-group-head, .int-sub, .int-item, .film, .artist,
        .piece, .int-quote, .friend, .game-body, .proj-body, .int-link,
        .book-quote, .book-info){
    -webkit-mask-image: none;
    mask-image: none;
  }
  .is-int-theme body[data-route="/interests"]
    :is(.footer, .footer a){
    color: CanvasText;
    text-shadow: none;
  }
  .is-int-theme body[data-route="/interests"] .footer{ background: Canvas }
  /* The phone footer's height is there to show the painting, which this mode
     hides: without it the page would end on a blank band. */
  .is-int-theme body[data-route="/interests"] .footer{ min-height: 0 }
  /* The highlighter and the sheen are painted, not system colours; the
     cover keeps an edge instead of its shadow. */
  .is-int-theme body[data-route="/interests"] .book-title{ background: none }
  .is-int-theme body[data-route="/interests"] .book-cover::after{ display: none }
  .is-int-theme body[data-route="/interests"] .book-cover{
    box-shadow: none;
    border: 1px solid CanvasText;
  }
  .is-int-theme body[data-route="/interests"] :is(.int-tools, .int-panel:not([hidden])){
    background: Canvas;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-color: CanvasText;
  }
  .is-int-theme body[data-route="/interests"]
    :is(.int-tool, .int-panel-body button, .int-panel-foot .int-copy){
    background: ButtonFace;
    border: 1px solid ButtonText;
    color: ButtonText;
  }
  .is-int-theme body[data-route="/interests"]
    :is(.int-tool[aria-pressed="true"], .int-panel-body button[aria-pressed="true"],
        .int-panel-body button[aria-checked="true"]){
    background: Highlight;
    color: HighlightText;
  }
  .is-int-theme body[data-route="/interests"] .int-ctl[data-ctl="slider"]{
    border: 1px solid CanvasText;
  }
  .is-int-theme body[data-route="/interests"] .int-ctl-fill{ background: Highlight }
  .is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch){
    border: 1px solid ButtonText;
    background: ButtonFace;
  }
  .is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)[aria-checked="true"]{
    background: Highlight;
  }
  .is-int-theme body[data-route="/interests"] .int-panel-body [role="switch"]:not(.int-ctl--switch)::after{
    background: ButtonText;
  }
  .is-int-theme body[data-route="/interests"] .int-switch{
    border: 1px solid ButtonText;
    background: ButtonFace;
  }
  .is-int-theme body[data-route="/interests"] .int-ctl--switch[aria-checked="true"] .int-switch{ background: Highlight }
  .is-int-theme body[data-route="/interests"] .int-switch-knob{ background: ButtonText }
  /* the system palette is the point of this mode; the column filter would grey it */
  .is-int-theme body[data-route="/interests"] .int-sheet-fill{ filter: none !important }
  .is-int-theme body[data-route="/interests"] .int-tile{ display: none }
  /* Back to text buttons, two to a row as before, so the longer names do
     not wrap. The choice gets a Highlight outline: a Highlight fill would put
     the name on a system backplate and leave a blank block. */
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles{ grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles .int-seg-btn{
    min-height: 32px;
    padding: 7px 10px;
    border: 1px solid ButtonText;
  }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-seg--tiles .int-seg-btn:is([aria-checked="true"], .is-on){
    border-color: Highlight;
    outline: 3px solid Highlight;
    outline-offset: 1px;
  }
}

/* ============================================
   TOUCH SIZES
   Every touch screen, not just phones: the mouse-sized controls above were
   only enlarged at 768 and below, and even there later rules undid some of
   it. Last in the file so it beats all of them.
   ============================================ */
@media (pointer: coarse){
  .is-int-theme body[data-route="/interests"] .int-tool{ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] :is(.int-panel-reset, .int-panel-close){ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-dice{ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] .int-color-swatch{ width: 40px; height: 40px }
  .is-int-theme body[data-route="/interests"] .int-ctl[data-ctl="slider"]{ height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-face{ min-height: 40px }
  .is-int-theme body[data-route="/interests"] :is(.int-panel-body, .int-panel-foot) .int-copy{ min-height: 40px }
  .is-int-theme body[data-route="/interests"] .int-panel-body .int-sw{ min-height: 34px }
}

/* ============================================
   SCRUBBING
   While a slider is held (js/interests-theme.js, "scrubbing") the rest of
   the right-hand column steps aside, so the change is seen on the page and
   not through the panel: the panel's glass, border and shadow, its header
   and footer, every other control, the tools rail and the phone's scrim fade
   out, and the slider stays where it is on a glass of its own. Out quickly,
   back a little more slowly, the way iOS clears Control Center while its
   brightness is dragged. The transitions are only declared for the gesture
   (.is-scrubbing, then .is-unscrubbing on the way back), so each element
   keeps its own the rest of the time.
   ============================================ */
.is-int-theme body[data-route="/interests"] #int-ui:is(.is-scrubbing, .is-unscrubbing) .int-panel:not([hidden]){
  transition: opacity .3s ease, translate .3s cubic-bezier(.22,1,.36,1),
              background-color .3s ease, border-color .3s ease, box-shadow .3s ease,
              -webkit-backdrop-filter .3s ease, backdrop-filter .3s ease;
}
.is-int-theme body[data-route="/interests"] #int-ui.is-scrubbing .int-panel:not([hidden]){
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition-duration: .18s;
}
.is-int-theme body[data-route="/interests"] #int-ui:is(.is-scrubbing, .is-unscrubbing)
  :is(.int-tools, .int-panel-head, .int-panel-foot, .int-panel-backdrop, .int-pane > :not(.is-scrub)){
  transition: opacity .3s ease;
}
.is-int-theme body[data-route="/interests"] #int-ui.is-scrubbing
  :is(.int-tools, .int-panel-head, .int-panel-foot, .int-panel-backdrop, .int-pane > :not(.is-scrub)){
  opacity: 0;
  transition-duration: .18s;
}
.is-int-theme body[data-route="/interests"] #int-ui.is-scrubbing .int-panel-body{
  scrollbar-color: transparent transparent;
}
/* The held slider: its own light glass, since the panel's is gone. */
.is-int-theme body[data-route="/interests"] #int-ui:is(.is-scrubbing, .is-unscrubbing) .int-ctl[data-ctl="slider"]{
  transition: background-color .3s ease, box-shadow .3s ease;
}
.is-int-theme body[data-route="/interests"] #int-ui.is-scrubbing .int-ctl.is-scrub{
  background-color: rgb(255 255 255 / .9);
  box-shadow: inset 0 0 0 1px rgb(15 23 42 / .1), 0 1px 2px rgb(15 23 42 / .08);
  transition-duration: .18s;
}
@media (forced-colors: active){
  .is-int-theme body[data-route="/interests"] #int-ui.is-scrubbing .int-ctl.is-scrub{
    background-color: Canvas;
    box-shadow: none;
  }
}

/* ============================================
   PRINT
   Paper has no painting and no glass. Printing Main gives black type on
   white from the top of the first page, without the islands, the tools, the
   tip, the hover card or the cursor trail: the FORCED COLOURS block above,
   with fixed ink instead of system colours. Entrances that wait for a scroll
   (the section heads, the typed name) print finished, and no entry is split
   across two pages.
   ============================================ */
@media print{
  @page{ margin: 16mm 16mm 18mm }
  html, body{ background: #fff !important }

  /* Screen-only: the painting and every fixed or interactive layer. */
  #int-stage, .site-nav, #int-ui, #int-picker, .int-preview, .skip-link,
  .zenith-trail-canvas, .zenith-storm-layer, .zenith-storm-toast, .pixel-leaf,
  .int-sheet-blend, .int-sheet-fill, .int-sheet-canvas,
  .int-daily-next, .int-daily-go, .int-link-go, .proj-go{ display: none !important }

  /* The column: top of page one, the page's full measure, black ink. The
     theme writes its colours on <html>; re-pointing them here wins for
     everything inside the column. */
  .is-int-theme body[data-route="/interests"] .int-sheet{
    --int-ink: #111 !important;
    --int-ink-light: #111 !important;
    --int-shadow-light: none !important;
    --int-distress: 0% !important;
    --int-size: 11pt !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }
  .is-int-theme body[data-route="/interests"] .int-shell{ padding-inline: 0 !important }
  .is-int-theme body[data-route="/interests"] .int-main{
    max-width: none !important;
    color: #111 !important;
  }
  .is-int-theme body[data-route="/interests"] .int-main,
  .is-int-theme body[data-route="/interests"] .int-main *,
  .is-int-theme body[data-route="/interests"] .footer{ text-shadow: none !important }
  .is-int-theme body[data-route="/interests"] .int-distress{
    -webkit-mask-image: none !important;
    mask-image: none !important;
  }
  .is-int-theme body[data-route="/interests"] .footer,
  .is-int-theme body[data-route="/interests"] .footer a{
    color: #555 !important;
    background: none !important;
    min-height: 0 !important;
  }
  .book-cover{ box-shadow: none !important }
  .book-cover::after{ display: none !important }

  /* The two contact links under the name keep their address on paper. */
  .int-intro-links a[href^="http"]::after{ content: " (" attr(href) ")"; font-size: .85em }

  /* A heading stays with what follows it, and no entry is cut in two. */
  h1, h2, h3, h4, .int-group-head, .section-head{ break-after: avoid; page-break-after: avoid }
  .int-intro, .int-hello, .int-daily, .int-item, .int-row, .int-excerpt, .int-quote,
  .proj, .xp, .book, .film, .artist, .piece, .game, .friend, blockquote, figure{
    break-inside: avoid;
    page-break-inside: avoid;
  }
  img{ max-width: 100% !important }

  .int-reveal{ opacity: 1 !important; transform: none !important; transition: none !important }
  .int-intro-name, .int-intro-name .type-ch{ visibility: visible !important; animation: none !important }
  .int-intro-name .type-ch::after{ display: none !important }
}
