/* =============================================================================
 * CAT MODE, PART TWO ,  paw prints and easter eggs.
 * ===========================================================================
 * Appended to the layer in cat-mode.css. Everything here is scoped to
 * [data-catmode] and inert in the other five aesthetics.
 *
 * THE VISITOR'S VOICE
 * The corner visitor speaks in a real pixel face, self-hosted from Google Fonts
 * (Silkscreen, SIL Open Font License) rather than linked, for the same reason
 * the loading cat's sprite is vendored: a visitor that cannot be drawn because
 * a CDN is slow or blocked is worse than no visitor, and this app already has
 * font fetches that fail. 15 KB for both weights, latin subset only, and the
 * browser does not download it at all until something on screen asks for it , 
 * which, being scoped to [data-catmode], is nobody until the cat turns up. */

@font-face{
  font-family:'Silkscreen';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('/assets/fonts/Silkscreen-Regular.woff2') format('woff2');
}
@font-face{
  font-family:'Silkscreen';
  font-style:normal;
  font-weight:700;
  font-display:swap;
  src:url('/assets/fonts/Silkscreen-Bold.woff2') format('woff2');
}

/* ===========================================================================*/
 *
 * PAW PRINTS
 * A fixed full-viewport layer behind the app, repeating a paw print. One
 * repeating background image on a single element, not one element per print:
 * fifty absolutely-positioned nodes is fifty nodes to composite every frame, and
 * this is decoration that has to be free.
 *
 * Deliberately a low-contrast multiply-ish tint. High opacity turns a working
 * document into wallpaper, and the point was atmosphere, not a pattern you read
 * past.
 *
 * EASTER EGGS
 * Four, all opt-out and all non-blocking:
 *
 *   1. KONAMI CODE   Up Up Down Down Left Right Left Right B D Enter. A cat walks
 *                    across the bottom of the screen doing a victory lap.
 *   2. MEOW          Ctrl/Cmd + M. The whole interface meows.
 *   3. CATNIP        Click the wordmark seven times. The wordmark grows ears.
 *   4. NINE LIVES    Type "9 lives". A toast says so. Nothing more, because the
 *                    best easter egg is the one that does not keep going.
 *
 * Every one respects prefers-reduced-motion, and every one announces itself in a
 * toast rather than failing silently ,  an easter egg nobody can find is a bug.
 * =========================================================================== */

html[data-catmode]{}

/* ---- paw prints ------------------------------------------------------- */
html[data-catmode]::before{
  content:'';
  position:fixed;
  inset:0;
  z-index:0;
  pointer-events:none;
/* Seven prints per tile, each rotated to a different heading ,  up, right,
   down, left and the diagonals. The tile used to carry two, at -22 and +16, so
   every paw on the screen faced roughly the same way; that is not a random
   scatter, it is a printed pattern, and the eye finds the two headings
   immediately. One print per tile is worse still ,  a mechanical repeat. A
   pattern only reads as incidental when the direction, the size and the
   position all vary, and all three have to vary *within* the tile: one tile is
   repeated across the viewport, so anything constant in it is constant
   everywhere.

     width/height on the root are load-bearing. Without them the SVG has no
     intrinsic size, and as a background image Chrome resolved it against the
     default replaced-element box instead of the tile. They also mean the tile
     is 96px of artwork, which is why it is drawn at 118px below. */
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Cg fill='%238a6a48'%3E%3Cg transform='translate(10.00 9.00) rotate(0 12 12) scale(0.5)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(46.48 7.48) rotate(92 12 12) scale(0.46)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(74.24 24.24) rotate(178 12 12) scale(0.48)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(18.00 39.00) rotate(268 12 12) scale(0.5)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(46.48 54.48) rotate(34 12 12) scale(0.46)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(76.00 68.00) rotate(211 12 12) scale(0.5)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3Cg transform='translate(8.72 72.72) rotate(118 12 12) scale(0.44)'%3E%3Cellipse cx='6.7' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='6.6' rx='2.7' ry='3.6'/%3E%3Cellipse cx='17.3' cy='8' rx='2.6' ry='3.4'/%3E%3Cellipse cx='12' cy='16.4' rx='5.6' ry='4.6'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-repeat:repeat;
  background-size:118px 118px;
  /* An SVG data URI cannot carry opacity, so the tint is burnt into the fill and
     the layer's own opacity does the rest. This is deliberately near the floor:
     the prints should be something you notice once and then stop seeing, or they
     become a texture behind the text you are trying to read. */
  opacity:.055;
  mix-blend-mode:multiply;
}
html[data-skin="midnight"][data-catmode]::before,
html[data-skin="contrast"][data-catmode]::before{
  /* multiply on a dark page only makes it darker, so the dark themes get a light
     tint and `screen` instead of a dark one and `multiply`. */
  mix-blend-mode:screen;
  opacity:.04;
}

/* ---- 1. konami code --------------------------------------------------- */
/* A jointed cat, not a shape sliding sideways.

   The first version translated one static cat-head silhouette across the screen,
   which reads as an icon being dragged, not as an animal. This is built from
   parts and animated separately, which is the same idea as the sprite-sheet
   `steps()` rig ,  legs stepping, body riding on them, head leading slightly,
   tail counterbalancing ,  but drawn here so there is no third-party asset and
   nothing to license.

   GAIT. Cats walk in a lateral sequence: the left fore and the right hind move
   together, and the other pair moves with them half a beat later. So the legs
   are two phase groups, not four independent ones, which is what makes the legs
   pair off instead of scissoring at random.

   SPEED. Six and a half seconds across the screen read as a conveyor belt. It is
   a stroll now ,  sixteen seconds, with a little over a second per stride, which
   is roughly a cat ambling rather than a cat trotting. */
/* From the right, walking left. It used to enter from the left edge and cross
   to the right, which put its arrival under the reading hand and its departure
   off the edge nobody was looking at. Coming in from the right means it arrives
   where the eye already is and leaves toward the centre.

   The flip is on the svg and not the wrapper: the wrapper transform is the
   stroll itself, and stacking a scale on it would fight the keyframes. */
/* =============================================================================
   The visitor that turns up in the corner.

   Nine of them, and four are not cats ,  see assets/vendor/cat-visitor/
   ATTRIBUTION.md for the cast and for why that is the point rather than a
   mistake. Nothing here distinguishes one from another, on purpose: a visitor
   that gave itself away in the styling would give the whole thing away.

   Deliberately NOT the loading indicator's sprite. That is a black Nabors walk
   cycle and it stays that way, because it means "something is working". This
   one means "something has arrived". Using the same art for both would make a
   decorative visit indistinguishable from a stalled request, and the two would
   contradict each other whenever one happened during the other.

   It arrives rather than walks ,  see cat-pop-in below, which fades and does
   nothing else. A crossing visitor is a moving thing you end up watching
   instead of reading, and this one has something to say.

   Full colour throughout, so unlike every other cat in this app these take no
   filter. Collapsing them with `brightness(0) invert(...)` would turn the one
   thing here that has faces into silhouettes, and the art is the entire point.
   =========================================================================== */
html[data-catmode] .pop-cat{
  position:fixed;
  z-index:10002;
  /* 78px from a 150px source: a corner visitor, not a character. At 132 she
     dominated the bottom-right quadrant and covered the Library tab; she is
     meant to be something you notice a second after the work, not something
     you move out of the way first. `image-rendering:pixelated` is deliberately
     absent ,  she is not pixel art, and nearest-neighbour on a smooth drawing
     makes her look chipped. */
  width:78px;
  height:78px;
  pointer-events:none;
  animation:cat-pop-in .42s cubic-bezier(.2,1.5,.4,1) both,
            cat-pop-out .5s ease 6.6s both;
}
/* One fixed corner. The other three were for a cat that wandered; this one does
   not, and a spot you already glance at is worth more than a surprise you
   never learn. */
/* Flush into the corner. It used to sit 70px up, which was a guess made when
   the chat FAB was anchored down here ,  that button is `position:static` and
   lives in the top nav, so the clearance was for a control that is not in this
   corner, and the visitor floated a cat's-width above the floor of the screen
   for no reason.

   The one thing actually down here is the whiteboard's page bar plus its
   calculator button, which between them run the full width of the corner. The
   visitor still keeps the floor on every view; on the whiteboard it stands
   clear to the LEFT of that bar, offset measured from the bar in cat-eggs.js
   rather than guessed here, because the bar grows with its page label. */
html[data-catmode] .pop-cat.is-br{right:14px;bottom:8px}

html[data-catmode] .pop-cat .pc-sprite{
  width:100%;
  height:100%;
  object-fit:contain;
  object-position:bottom center;
}
/* Nearest-neighbour ONLY on the art that was drawn on a grid. The sprite sheet
   rule at the top of this file says the same thing about the loading cat, for
   the same reason: pixel art at 78px needs every source pixel to survive, and
   the flat illustrations need their curves left alone. The class is set per
   image in cat-eggs.js rather than guessed from the file name. */
html[data-catmode] .pop-cat .pc-sprite.is-px{image-rendering:pixelated}

/* Arrival, not travel. These keyframes used to lift the visitor 14px and scale
   it up from .7, which is a thing crossing a distance ,  and a visitor who
   crosses a distance is a thing you watch instead of read. It arrives, it is
   there, it goes. */
@keyframes cat-pop-in{
  from{opacity:0}
  to{opacity:1}
}
@keyframes cat-pop-out{
  from{opacity:1}
  to{opacity:0}
}

/* The remark. A cloud that WRAPS ,  an earlier attempt pinned it to one line
   with an ellipsis, which cut most sentences short and made the whole thing
   read as a truncated tooltip rather than a cat saying something. */
html[data-catmode] .pop-cat .pc-bubble{
  position:absolute;
  /* Anchored to the RIGHT edge of the cat, not centred on it.

     Centring was wrong by about 60px and the sentence was simply cut off by
     the side of the window. The cat sits 22px from the right edge, so its centre
     is 61px in; a 250px bubble centred there has its right-hand 125px reaching
     64px PAST the edge of the screen. Growing leftwards from the cat's right
     edge keeps the whole remark visible at every viewport width, and it costs
     nothing visually ,  the cat is 78px wide, so the cloud sits over its shoulder
     rather than over its head. */
  left:auto;
  right:-6px;
  width:max-content;
  max-width:min(58vw,250px);
  padding:7px 12px;
  border-radius:15px;
  border:1px solid var(--border);
  background:var(--glass-bg);
  -webkit-backdrop-filter:var(--glass-blur,none);
  backdrop-filter:var(--glass-blur,none);
  color:var(--fg);
  /* The pixel face, to match the art it is attached to. Silkscreen runs tall
     and wide for its size ,  it is drawn on a 4px grid and cannot be nudged
     below that without smearing ,  so the size is set here rather than inherited
     from --fs-xs, and the line-height is opened up to match: at 1.5 the
     ascenders of one line touch the descenders of the next and the text reads
     as a block of noise. */
  font:400 11px/1.75 'Silkscreen',ui-monospace,monospace;
  letter-spacing:0;
  text-align:left;
  text-wrap:balance;
  opacity:1;
  transform:none;
}
/* The cloud sits above her: she is in the bottom-right corner, so anything
   placed above the cat is on the readable part of the screen and nothing
   overlaps the corner controls. */
html[data-catmode] .pop-cat .pc-bubble{bottom:112%}
/* There is no longer a silent visit. The rule that hid the cloud for one is
   gone with it: it turns up, it has something to say, and a visitor that
   arrives and says nothing is just a picture in the corner. */

@media (max-width:560px){
  html[data-catmode] .pop-cat{width:64px;height:64px}
  html[data-catmode] .pop-cat .pc-bubble{max-width:50vw;font-size:11px}
}

@media (prefers-reduced-motion:reduce){
  /* It still arrives and still says its piece ,  it simply does not spring.
     Dropping the feature entirely would remove it from anyone who asked for
     less movement rather than for less of the app. */
  html[data-catmode] .pop-cat{
    animation:cat-pop-fade .3s linear both,cat-pop-out .4s linear 6.6s both;
  }
}
@keyframes cat-pop-fade{from{opacity:0}to{opacity:1}}
html[data-catmode] .konami-cat{
  position:fixed;
  left:0;
  bottom:10px;
  z-index:10000;
  width:76px;
  height:48px;
  pointer-events:none;
  color:var(--accent);
  animation:konami-stroll 16s linear 1;
}
html[data-catmode] .konami-cat svg{
  width:100%;
  height:100%;
  overflow:visible;
  transform:scaleX(-1);
}
html[data-catmode] .konami-cat .kc-body{
  fill:currentColor;
  animation:kc-bob 1.05s ease-in-out infinite;
}
html[data-catmode] .konami-cat .kc-tail{
  fill:currentColor;
  transform-origin:14px 22px;
  animation:kc-tail 1.4s ease-in-out infinite;
}
html[data-catmode] .konami-cat .kc-head{
  fill:currentColor;
  transform-origin:52px 19px;
  animation:kc-head 1.05s ease-in-out infinite;
}
html[data-catmode] .konami-cat .kc-ear{fill:currentColor}
html[data-catmode] .konami-cat .kc-leg{
  fill:currentColor;
  transform-box:fill-box;
  transform-origin:50% 0;
}
html[data-catmode] .konami-cat .kc-leg.a{animation:kc-step-a 1.05s ease-in-out infinite}
html[data-catmode] .konami-cat .kc-leg.b{animation:kc-step-b 1.05s ease-in-out infinite}
@keyframes kc-step-a{
  0%,100%{transform:rotate(-15deg)}
  50%{transform:rotate(15deg)}
}
@keyframes kc-step-b{
  0%,100%{transform:rotate(15deg)}
  50%{transform:rotate(-15deg)}
}
@keyframes kc-bob{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(-1.6px)}
}
@keyframes kc-head{
  0%,100%{transform:rotate(2.5deg)}
  50%{transform:rotate(-2.5deg)}
}
@keyframes kc-tail{
  0%,100%{transform:rotate(-7deg)}
  50%{transform:rotate(9deg)}
}
/* Right edge to left edge. The cat faces left because the svg is flipped. */
@keyframes konami-stroll{
  from{transform:translateX(calc(100vw + 90px))}
  to{transform:translateX(-90px)}
}

/* ---- 2. meow ---------------------------------------------------------- */
html[data-catmode].is-meowing body,
html[data-catmode].is-meowing #topNav{
  animation:meow .5s ease-in-out 2;
}
@keyframes meow{
  0%,100%{transform:translateY(0) rotate(0)}
  25%{transform:translateY(-5px) rotate(-.35deg)}
  75%{transform:translateY(-5px) rotate(.35deg)}
}

/* ---- 3. catnip: ears on the wordmark ----------------------------------- */
html[data-catmode] .brand.is-catinip{position:relative}
html[data-catmode] .brand.is-catinip::before,
html[data-catmode] .brand.is-catinip::after{
  content:'';
  position:absolute;
  top:-4px;
  width:11px;
  height:13px;
  background:var(--accent);
  /* Two triangles, because `clip-path` on a decorative overlay is cheaper than
     another two elements and keeps the ears out of the accessibility tree. */
  clip-path:polygon(50% 0, 100% 100%, 0 100%);
}
html[data-catmode] .brand.is-catinip::before{left:6px;transform:rotate(-16deg)}
html[data-catmode] .brand.is-catinip::after{right:6px;transform:rotate(16deg)}

/* Reduced motion: the prints stay, everything that moves stops moving. The
   eggs still fire and still say so ,  they just do not animate. */
@media (prefers-reduced-motion:reduce){
  html[data-catmode] .konami-cat{animation:none;left:50%;transform:translateX(-50%)}
  html[data-catmode].is-meowing body,
  html[data-catmode].is-meowing #topNav{animation:none}
}

/* =============================================================================
   The paw itself.

   One path, reused as a background image four times over ,  the whiteboard
   grid, the taskbar trail, the footprints and the little silhouettes. Inlined
   rather than fetched so nothing here can 404, and so it inherits currentColor
   where that matters.
   =========================================================================== */
:root{
  --cat-paw:"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cg fill='%23888'%3E%3Cellipse cx='16' cy='21.5' rx='7' ry='5.3'/%3E%3Cellipse cx='7.4' cy='13.4' rx='3.1' ry='3.9'/%3E%3Cellipse cx='13.4' cy='8.2' rx='3.1' ry='4'/%3E%3Cellipse cx='19.6' cy='8.2' rx='3.1' ry='4'/%3E%3Cellipse cx='25.4' cy='13.4' rx='3.1' ry='3.9'/%3E%3C/g%3E%3C/svg%3E";
  /* Wrapped in url() where --cat-paw is not, and that one word is the whole
     bug. `background-image` accepts a bare <string>, so --cat-paw worked as-is.
     `mask-image` accepts only <url> or none ,  a substituted string is not a
     valid <mask-source>, so the whole declaration was dropped and the property
     fell back to its initial `none`.

     Nothing looked broken in the CSS and nothing threw: the taskbar paws simply
     kept their accent background with no mask at all, which is a coloured
     19px square. They were reported as "squares" rather than as a failure to
     load, which is why this survived so long ,  the previous note in this file
     blamed a missing width/height on the SVG root, and adding those would not
     have helped, because the declaration was never parsed in the first place. */
  --cat-paw-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='%23000'%3E%3Cellipse cx='16' cy='21.5' rx='7' ry='5.3'/%3E%3Cellipse cx='7.4' cy='13.4' rx='3.1' ry='3.9'/%3E%3Cellipse cx='13.4' cy='8.2' rx='3.1' ry='4'/%3E%3Cellipse cx='19.6' cy='8.2' rx='3.1' ry='4'/%3E%3Cellipse cx='25.4' cy='13.4' rx='3.1' ry='3.9'/%3E%3C/g%3E%3C/svg%3E");
}


/* --- the taskbar ------------------------------------------------------- */
/* A few paw marks set along the bar like a printed pattern: tilted, spaced
   unevenly, in the accent colour. Positions and angles are chosen in JS so
   they do not read as a repeat. */
/* The paws are absolutely positioned children of the bar, and an absolutely
   positioned element is placed against its nearest POSITIONED ancestor. The
   bar is `position:fixed`, which is positioned, so the paws land on the bar
   either way - and this rule used to take the bar out of the viewport's
   flow entirely (`position:relative`), which on a phone meant the taskbar
   scrolled away with the page instead of staying at the top of it. */
html[data-catmode] .top-nav{position:fixed}
html[data-catmode] .cat-bar-paw{
  position:absolute;z-index:0;width:19px;height:19px;
  /* Shape comes from the mask and the colour from the background, NOT from
     the grey paw image: an image on top of the accent colour would simply
     repaint it grey, which is what the first attempt did. */
  background-color:var(--accent);
  -webkit-mask-image:var(--cat-paw-mask);
  mask-image:var(--cat-paw-mask);
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  transform:rotate(var(--rot,0deg)) scale(var(--sc,1));
  opacity:.5;
  pointer-events:none;
}

/* --- leaving messages -------------------------------------------------- */
html[data-catmode] .cat-bye{
  position:fixed;z-index:9900;left:50%;top:18px;
  transform:translateX(-50%);
  display:flex;align-items:center;gap:9px;
  padding:9px 15px;border:1px solid var(--line);border-radius:999px;
  background:var(--panel);color:var(--fg);
  box-shadow:0 14px 40px rgba(0,0,0,.45);
  font:500 13px/1 var(--sans);
  animation:cat-bye-in .4s ease both;
}
html[data-catmode] .cat-bye::before{
  content:"";width:20px;height:20px;
  background-image:var(--cat-paw);background-size:contain;
  opacity:.9;
}
html[data-catmode] .cat-bye.is-out{animation:cat-bye-out .5s ease both}
@keyframes cat-bye-in{from{opacity:0;transform:translate(-50%,-12px)}to{opacity:1;transform:translate(-50%,0)}}
@keyframes cat-bye-out{from{opacity:1}to{opacity:0}}
/* Reduced motion, LAST.

   Every rule above is the same specificity as the animation it is meant to
   cancel, so this block has to come after all of them. Written in the middle
   of the file it looked correct and did nothing at all: the walker kept
   animating for exactly the people who had asked it not to. */
@media (prefers-reduced-motion:reduce){
  html[data-catmode] .konami-cat{animation:none;left:50%;transform:translateX(-50%)}
  html[data-catmode].is-meowing body,
  html[data-catmode].is-meowing #topNav{animation:none}
    html[data-catmode] .cat-bye{animation:none}
}