/* ==========================================================================
   KodeYak — the closing screen (footer)

   The masthead's answer at the other end of the page. Self-contained like
   nav.css and hero.css, so it can be tuned or dropped without touching the
   background. Colour and motion come from tokens.css — never a raw hex here.

   THE SHAPE. The masthead's own underside profile, moved to the bottom of the
   document: a raised PLINTH on the right, exactly as thick as the nav rail, and
   a chamfer stepping down to the body on the left. Paper shows in the notch.

     ─────────────────────────────────────────────────────  paper
                              ┌──────────────────────────┐  <- plinth top
                             ╱   (--ft-rise = --mh-short)
      ┌─────────────────────┘  <- body top
      │                                                  │
      │   contact, links, the effects control, colophon  │
      └──────────────────────────────────────────────────┘  page bottom

   WHY IT LINES UP AND CANNOT DRIFT. Every number here is derived from the
   masthead's own four variables, which nav.css declares on :root precisely so
   things outside it can read them. The plinth is --mh-short thick because that
   IS the nav rail's thickness, and the step happens at --mh-shoulder +
   --mh-chamfer because that is where the masthead's chamfer finishes and its
   thin rail begins. So the vertical line where "the raised part starts" is one
   number shared by both ends of the page. Type a pixel value into either and
   they part company at the first breakpoint — the 720px media query in nav.css
   moves all four, and this file follows for free because it never restates them.

   This is the same rule the hero rail follows ("screens meet at nodes, and node
   positions are derived, never nudged"); the only difference is that the rail's
   nodes come from the display panel and these come from the masthead.          */

.ky-footer {
  /* The plinth's rise IS the nav rail's thickness. Not "about the same". */
  --ft-rise: var(--mh-short);

  /* 45°, so the run equals the rise. The system's stated chamfer angle — two
     cuts can only read as the same cut if they agree what a cut is. The
     masthead's own chamfer is 30px across a 28px step, i.e. 47°, which is
     inside the tolerance of the eye at this scale; matching its ANGLE exactly
     would mean dividing one length by another, which calc() cannot do. */
  --ft-chamfer: var(--ft-rise);

  /* Where the raised region begins — read off the masthead, not measured. */
  --ft-node: calc(var(--mh-shoulder) + var(--mh-chamfer));

  /* Room ABOVE the clipped layers for the grown edge to live in, the mirror of
     the masthead's extra room below. clip-path clips and never expands, so
     without this the hairline is cut off flush at the top of each level and
     disappears exactly where it matters most. */
  --ft-over: 8px;

  /* The contact column's width. Declared here rather than left to the content,
     because the strand layer cuts off before it and needs a number to cut at —
     see --gb-stop. Wide enough for "info@kodeyak.com" at --ky-text-body-lg with
     room to spare; the block is right-aligned inside it. */
  --ft-contact-w: 360px;

  position: relative;
  margin-top: var(--ky-space-16);
  /* NO blanket padding-top. The notch has to be cleared by anything on the LEFT,
     but the contact block deliberately reaches up into the plinth on the right,
     so the clearance is applied per column below rather than to the whole box. */
}

/* Both layers share ONE clip, for the same reason the masthead's do: the lit
   edge has to trace the chamfer, which a border cannot do, and two independently
   written outlines are how an edge ends up a pixel off the shape it traces.
   --ft-grow pushes the boundary up by a hair on the edge layer, and the sliver
   that peeks out above the panel IS the lit line.

   Content never goes inside these — a clip-path clips the element's whole
   rendering, focus rings included. Both are decorative and hidden from AT.      */
.ky-footer__edge,
.ky-footer__panel {
  position: absolute;
  inset: calc(var(--ft-over) * -1) 0 0;
  pointer-events: none;
  clip-path: polygon(
    0                                          calc(var(--ft-over) + var(--ft-rise) - var(--ft-grow, 0px)),
    calc(var(--ft-node) - var(--ft-chamfer))   calc(var(--ft-over) + var(--ft-rise) - var(--ft-grow, 0px)),
    var(--ft-node)                             calc(var(--ft-over) - var(--ft-grow, 0px)),
    100%                                       calc(var(--ft-over) - var(--ft-grow, 0px)),
    100% 100%,
    0    100%
  );
}

/* Lit from the RIGHT, like every other edge in the system — the bloom is the
   scene's light source and it sits right of frame, so an edge that caught
   brightest on the left would be lit by nothing. Same ramp as the masthead's,
   so the two read as the same machined edge seen twice. */
.ky-footer__edge {
  --ft-grow: 1.5px;
  background: linear-gradient(90deg,
    rgba(var(--ky-rgb-gridlit), .20) 0%,
    rgba(var(--ky-rgb-gridlit), .34) 42%,
    rgba(var(--ky-rgb-bone), .52) 76%,
    rgba(var(--ky-rgb-bone), .68) 100%);
}

/* OPAQUE, and that is the one place this deliberately parts from the masthead.
   The masthead is translucent over the artwork with a real backdrop-filter, and
   copying that here would blur cream paper to no purpose — .ky-paper paints an
   opaque ground behind its children, which is the same trap that forces the hero
   rail to be a sibling of .ky-paper rather than a child. So this follows
   .ky-panel instead: a glass plate SET INTO the paper, opaque, carrying its own
   lattice and its own cursor wash.

   Layer order matches .ky-panel exactly — wash, lattice, corner sheen, base —
   so the two glass surfaces on the page are the same material.                  */
.ky-footer__panel {
  background-color: var(--ky-glass-900);
  background-image:
    radial-gradient(circle calc(var(--ky-bloom) * 1.25) at var(--ky-bloom-x, -9999px) var(--ky-bloom-y, -9999px), rgba(var(--ky-rgb-fiber), var(--ky-bloom-wash, .14)), transparent 72%),
    repeating-linear-gradient(to right,  rgba(var(--ky-rgb-gridlit), .15) 0 1px, transparent 1px var(--ky-grid-major)),
    repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-gridlit), .15) 0 1px, transparent 1px var(--ky-grid-major)),
    radial-gradient(120% 180% at 100% 0%, rgba(var(--ky-rgb-bone), .055), transparent 58%),
    linear-gradient(180deg, var(--ky-glass-grad-a) 0%, var(--ky-glass-grad-b) 46%, var(--ky-glass-grad-c) 100%);
  /* +--ft-over ON THE VERTICALS, and it is not a fudge. lattice.js writes
     --ky-lattice-y and --ky-bloom-y relative to .ky-footer's own border box, but
     this layer is pulled --ft-over ABOVE that box to make room for the grown
     edge, so its coordinate origin sits --ft-over higher. Without the term the
     lattice lands 8px off the paper it is supposed to continue, and the wash
     lands 8px above the cursor — both small enough to look like nothing in
     particular and to be blamed on something else.

     Measured, because a term like this is exactly the kind that gets "tidied
     away" by someone who cannot see what it does: the paper's horizontal majors
     sit at y ≡ 74 (mod 120). With the term the footer's sit at 74. Without it
     they sit at 66. */
  background-position:
    0 var(--ft-over),
    var(--ky-lattice-x) calc(var(--ky-lattice-y) + var(--ft-over)),
    var(--ky-lattice-x) calc(var(--ky-lattice-y) + var(--ft-over)),
    0 0,
    0 0;
  background-repeat: no-repeat, repeat, repeat, no-repeat, no-repeat;
  /* ONE TILE = ONE PERIOD. A footer's height is content-driven and will never be
     a whole number of periods, so with background-size:auto the tile becomes the
     element and every line past the first handover is out of phase — the failure
     that reads as a faint dim patch rather than as a broken grid. See .ky-paper. */
  background-size:
    auto,
    var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
    auto, auto;
}

/* ---- The lit strands ------------------------------------------------------
   A strand has to enter this screen and leave it in register, exactly as it does
   through .ky-panel. Without this the paper's lit lattice ran down to the
   footer's edge and stopped dead, leaving a diffuse glow with no grid in it —
   which reads as a different kind of surface rather than as the same one.

   WHY THIS IS NOT .ky-panel'S RING, AND THE MEASUREMENT THAT SETTLED IT. A panel
   is ~460px wide with its text inset ~48px, so its protected rect is small and
   the lit perimeter is always inside the disc's 260px reach. This screen is
   full-bleed with a 1240px content column. Protecting the whole column was the
   first build here and it was DEAD: with the cursor anywhere near the middle the
   nearest unprotected pixel is ~100px of margin away, well outside the disc, so
   nothing lit at all. That is the "inert middle" CLAUDE.md warns about, at
   1240px instead of 460.

   So the protection is scoped to where copy ACTUALLY is:
     - the meta bar    bounded out by living inside the upper plate, since that
                       bar is almost all text and has no room to light anything
     - the contact col cut off before it, --ft-contact-w wide against the column's
                       right edge

   Everything left of that lights freely, which is most of the plate and is where
   a strand descending from the paper actually arrives.

   ⚠ THE LEFT OF THIS PLATE IS DELIBERATELY EMPTY AND DELIBERATELY UNPROTECTED.
   Copy dropped there will have strands run through it at 1.00:1 — invisible
   glyphs, not dim ones. When that column is filled, either widen the cut or put
   the new block on a leaf; the system's own rule is that copy on a blooming glass
   surface sits on something opaque.                                            */

.ky-footer__top { position: relative; }
.ky-footer__top-wrap { position: relative; z-index: 1; }

/* Outer: clip to the silhouette, so nothing paints into the notch. Separate
   element because the cut below would otherwise share a polygon with it. */
.ky-footer__bloomclip {
  position: absolute;
  inset: 0;
  pointer-events: none;
  clip-path: polygon(
    0                                        var(--ft-rise),
    calc(var(--ft-node) - var(--ft-chamfer)) var(--ft-rise),
    var(--ft-node)                           0,
    100%                                     0,
    100%                                     100%,
    0                                        100%
  );
}

.ky-footer__bloom {
  position: absolute;
  inset: 0;
  --gb-r: var(--ky-bloom);
  --gb-core: 1;     /* the 1px filament itself */
  --gb-halo: .34;   /* 5px companion  */
  --gb-glow: .16;   /* 13px companion */
  /* Where the strands must have stopped: the contact column's left edge, derived
     from the column rather than typed, so it tracks --ky-content-max and the
     gutter at every width. --gb-run is the falloff runway before it. */
  --gb-stop: calc(100%
    - (max(0px, (100% - var(--ky-content-max)) / 2) + var(--ky-gutter))
    - var(--ft-contact-w));
  --gb-run: 72px;
  mix-blend-mode: screen;
  /* disc ∩ ramp. One ramp, not the panel's H ∪ V: this layer is already bounded
     top, bottom and left by the plate itself, so the only thing left to fade
     away from is the contact column on the right. */
  -webkit-mask-image:
    radial-gradient(circle var(--gb-r) at var(--ky-bloom-x, -9999px) var(--ky-bloom-y, -9999px), #000 0%, rgba(0,0,0,.94) 38%, rgba(0,0,0,.52) 60%, rgba(0,0,0,.16) 76%, transparent 88%),
    linear-gradient(to right, #000 0, #000 calc(var(--gb-stop) - var(--gb-run)), transparent var(--gb-stop), transparent 100%);
          mask-image:
    radial-gradient(circle var(--gb-r) at var(--ky-bloom-x, -9999px) var(--ky-bloom-y, -9999px), #000 0%, rgba(0,0,0,.94) 38%, rgba(0,0,0,.52) 60%, rgba(0,0,0,.16) 76%, transparent 88%),
    linear-gradient(to right, #000 0, #000 calc(var(--gb-stop) - var(--gb-run)), transparent var(--gb-stop), transparent 100%);
  mask-composite: intersect;
  /* The hard floor. If mask-composite is ever ignored the layers default to
     `add` — a union — which would put strands over the contact block; this makes
     that failure "less glow" rather than "unreadable". A plain rect suffices
     here because the protected region runs to the edge, so it is a cut and not a
     hole — no ring winding needed. Only the standard property is declared:
     setting -webkit-mask-composite alongside it makes the two fight in Chrome. */
  clip-path: polygon(0 0, var(--gb-stop) 0, var(--gb-stop) 100%, 0 100%);
  /* No ambient disc. .ky-panel::after carries one, but this screen already paints
     the cursor wash UNRAMPED on .ky-footer__panel — that is the part measured
     safe over copy — and a second ramped copy would only double it. */
  background-image:
    repeating-linear-gradient(to right,  rgba(var(--ky-rgb-lit), var(--gb-core)) 0 1px, transparent 1px var(--ky-grid-major)),
    repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-lit), var(--gb-core)) 0 1px, transparent 1px var(--ky-grid-major)),
    repeating-linear-gradient(to right,  rgba(var(--ky-rgb-fiber), var(--gb-halo)) 0 5px, transparent 5px var(--ky-grid-major)),
    repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-fiber), var(--gb-halo)) 0 5px, transparent 5px var(--ky-grid-major)),
    repeating-linear-gradient(to right,  rgba(var(--ky-rgb-fiber), var(--gb-glow)) 0 13px, transparent 13px var(--ky-grid-major)),
    repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-fiber), var(--gb-glow)) 0 13px, transparent 13px var(--ky-grid-major));
  /* This layer's top IS the footer's top (the upper plate is the first thing in
     .ky-footer__inner, which is at inset 0), so --ky-lattice-x/y and
     --ky-bloom-x/y apply with NO shift — unlike .ky-footer__panel, which is
     pulled up by --ft-over and has to add it back. */
  background-position:
    var(--ky-lattice-x) var(--ky-lattice-y),
    var(--ky-lattice-x) var(--ky-lattice-y),
    calc(var(--ky-lattice-x) - 2px) var(--ky-lattice-y),
    var(--ky-lattice-x) calc(var(--ky-lattice-y) - 2px),
    calc(var(--ky-lattice-x) - 6px) var(--ky-lattice-y),
    var(--ky-lattice-x) calc(var(--ky-lattice-y) - 6px);
  /* One tile = one period — see .ky-paper. */
  background-repeat: repeat, repeat, repeat, repeat, repeat, repeat;
  background-size:
    var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
    var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
    var(--ky-grid-major) 100%, 100% var(--ky-grid-major);
  display: var(--ky-bloom-display, block);
}

/* ---- Content --------------------------------------------------------------
   Above the clipped layers, never inside them.                                */

.ky-footer__inner {
  position: relative;
  z-index: 1;
}

/* The face follows the ground with no help from this file: tokens.css routes
   .ky-glass h1/h2 to Tilt Neon and h3/h4/p/li to Ubuntu, so wrapping the footer
   in .ky-glass switches serif to sans across the whole block at once. That is
   the rule — ground decides the typeface, and it is never mixed inside one
   block — and it is why the heading is markup, not a font-family here.        */

/* ---- Upper plate ---------------------------------------------------------- */

/* Deliberately NOT .ky-section. That is clamp(space-16, 8vw, space-24) top and
   bottom — right for a content section, and roughly twice what a closing plate
   wants once the meta row moved out of it into its own plate below. */
.ky-footer__top-wrap {
  display: flex;
  align-items: flex-start;
  gap: var(--ky-space-8);
  /* Clears the notch for whatever lands on the LEFT later. The contact block
     opts out of it below, being the one thing that belongs above the line. */
  padding-top: var(--ft-rise);
  padding-bottom: clamp(var(--ky-space-8), 4vw, var(--ky-space-12));
}

/* Pushed right by auto margin rather than by a column rule, so adding a sibling
   before it needs no change here — it simply takes the space to the left. */
.ky-footer__contact {
  /* A DECLARED width, not shrink-to-fit, because the strand layer's cut is
     derived from it — --gb-stop has to know where this column starts, and a
     content-sized box cannot tell it. Text is right-aligned inside, so the
     column being wider than the longest line costs nothing visually. */
  width: min(var(--ft-contact-w), 100%);
  margin-inline-start: auto;
  /* Back up into the plinth, cancelling the wrap's clearance. The eyebrow below
     then occupies the raised band exactly. */
  margin-top: calc(var(--ft-rise) * -1);
  text-align: right;
}

/* Centred in the plinth's own height — the same idea as the masthead centring
   its lockup in the shoulder and its nav in the rail rather than in one shared
   row, and as the hero rail centring its eyebrow on the step it captions. */
.ky-footer__plinth-label {
  height: var(--ft-rise);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin: 0;
}

.ky-footer__mail { margin: var(--ky-space-3) 0 0; max-width: none; }

.ky-footer .ky-mail {
  color: var(--ky-link);
  font-size: var(--ky-text-body-lg);
  text-underline-offset: 4px;
}
.ky-footer .ky-mail:hover { color: var(--ky-link-hover); }

/* ---- The seam -------------------------------------------------------------
   Two plates butting up. A lit hairline, brightest on the RIGHT like every other
   edge in the system — the bloom is the light source and it sits right of frame.
   The dark line under it is what makes the joint read as two plates rather than
   as a rule drawn on one: a butt joint catches light on the upper lip and shows
   a shadow in the gap.                                                        */
.ky-footer__seam {
  height: 1px;
  background: linear-gradient(90deg,
    rgba(var(--ky-rgb-gridlit), .16) 0%,
    rgba(var(--ky-rgb-gridlit), .30) 42%,
    rgba(var(--ky-rgb-bone), .44) 76%,
    rgba(var(--ky-rgb-bone), .58) 100%);
  box-shadow: 0 1px 0 rgba(var(--ky-rgb-void), .55);
}

/* ---- The true footer ------------------------------------------------------
   Its own plate: a wash of Ink over the glass, so it sits a step deeper than the
   plate above and the seam has two different surfaces to separate. Height echoes
   the nav rail, which is the same rhyme the plinth makes at the other corner.  */
.ky-footer__base {
  background: rgba(var(--ky-rgb-void), .34);
}
.ky-footer__base-wrap {
  min-height: var(--mh-short);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--ky-space-3) var(--ky-space-8);
  padding-block: var(--ky-space-4);
}

/* IBM Plex Mono in the cool accent — the "data, eyebrows, labels, code" role
   from brand-identity.md, which is what a meta row is. Set at --ky-text-small
   rather than the spec's 11–12px because every item here is interactive or
   near it, and tracking eased to .14em to match: the eyebrow's .22em is tuned
   for a two-word label, not for a row of links. Colour is --ky-text-accent,
   which .ky-glass routes to cool-400 — the light blue in the specimen.        */
.ky-footer__base-wrap,
.ky-footer__base-wrap a,
.ky-switch {
  font-family: var(--ky-font-mono);
  font-size: var(--ky-text-small);
  font-weight: var(--ky-weight-medium);
  letter-spacing: .14em;
  text-transform: uppercase;
}

.ky-colophon { color: var(--ky-text-muted); margin: 0; }

.ky-footer-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--ky-space-3) var(--ky-space-8);
}
/* The masthead's hover rule, not a text-decoration: it LIGHTS rather than
   underlines, growing from the centre. Drawn on a pseudo-element because
   text-decoration cannot animate this way — the same reason nav.css draws it.
   Kept identical in colour and timing so the two rows of links read as one
   family; only the anchor's own padding differs, since this row is mono. */
.ky-footer-links a {
  color: var(--ky-text-accent);
  text-decoration: none;
  position: relative;
  padding-block: var(--ky-space-2);
  transition: color var(--ky-duration-fast) var(--ky-ease);
}
.ky-footer-links a:hover { color: var(--ky-cool-300); }
.ky-footer-links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg,
    transparent, rgba(var(--ky-rgb-gridlit), .9), transparent);
  transform: scaleX(0);
  transition: transform var(--ky-duration-base) var(--ky-ease);
}
.ky-footer-links a:hover::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .ky-footer-links a::after { transition: none; }
}

/* ---- The effects switch ---------------------------------------------------
   A real switch, not a pill button: it has two settled states rather than an
   action, and the knob's POSITION is what says which. Pushed to the end of the
   row, so the row reads meta-then-control.                                    */

.ky-fx {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--ky-space-3);
}

.ky-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--ky-space-3);
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: var(--ky-text-accent);
  line-height: 1.2;
}
.ky-switch:hover { color: var(--ky-cool-300); }

.ky-switch__track {
  position: relative;
  flex: 0 0 auto;
  width: 34px;
  height: 16px;
  border-radius: var(--ky-radius-pill);
  border: 1px solid rgba(var(--ky-rgb-bone), .30);
  background: rgba(var(--ky-rgb-void), .55);
  transition: border-color var(--ky-duration-fast) var(--ky-ease),
              background var(--ky-duration-fast) var(--ky-ease);
}

/* OFF is the base state and it is genuinely inert — no glow, dimmed knob. The
   control is showing you what the page looks like with the effect off. */
.ky-switch__knob {
  position: absolute;
  top: 50%;
  left: 2px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  transform: translateY(-50%);
  background: rgba(var(--ky-rgb-bone), .55);
  transition: left var(--ky-duration-base) var(--ky-ease),
              background var(--ky-duration-fast) var(--ky-ease),
              box-shadow var(--ky-duration-fast) var(--ky-ease);
}

/* ON: knob to the right, and it LIGHTS — in fiber, the bloom's own hue, because
   the honest preview of "background glow" is the background glow. This is not
   the accent signalling state (the rule that keeps warning off the brand hue);
   the state is carried by position, and the glow is a sample of the effect. */
.ky-switch[aria-checked="true"] .ky-switch__track {
  border-color: rgba(var(--ky-rgb-fiber), .45);
  background: rgba(var(--ky-rgb-fiber), .12);
}
.ky-switch[aria-checked="true"] .ky-switch__knob {
  left: calc(100% - 12px);
  background: rgb(var(--ky-rgb-lit));
  box-shadow: 0 0 9px 2px rgba(var(--ky-rgb-fiber), .55);
}

/* The knob slides; under reduced motion it simply appears in place. The effect
   this control governs is itself off in that case, so the switch is the only
   thing left that could move. */
@media (prefers-reduced-motion: reduce) {
  .ky-switch__knob, .ky-switch__track { transition: none; }
}

/* LOCKED. Under prefers-reduced-motion the glow is off unconditionally — there
   is no data-fx-ok escape for that one — so the switch cannot move and should
   not pretend otherwise. The script withdraws the action; this says so.

   Dimmed but still READABLE. WCAG exempts inactive controls from contrast, and
   that exemption is written for the `disabled` attribute — this control is only
   aria-disabled, so it keeps its place in the tab order and a reader can still
   reach it to find out why. Something they can reach has to stay legible.

   Dimmed with color-mix rather than `opacity`, for two reasons. It keeps a real
   solid computed colour, so tools/contrast reads what is actually painted —
   opacity on an ancestor makes the rendered glyph lighter than its computed
   colour and every measurement optimistic. And --ky-text-muted, the obvious
   reach, is WRONG here: on glass it resolves to bone-400 (luminance 196) while
   the live state is cool-400 (181), so "dimming" to it would make the locked
   control BRIGHTER than the working one.

   70% IS A MEASURED FLOOR, not a look. It lands the label at luminance 136
   against the live 181 — clearly quieter — while holding 5.20:1 on the base
   bar's ground. 62% looked right and measured 4.38:1, under AA on a control a
   keyboard user can still land on. Lower it and re-measure, or don't lower it. */
.ky-switch[aria-disabled="true"] {
  cursor: not-allowed;
  color: color-mix(in srgb, var(--ky-text-accent) 70%, var(--ky-glass-900));
}
.ky-switch[aria-disabled="true"]:hover {
  color: color-mix(in srgb, var(--ky-text-accent) 70%, var(--ky-glass-900));
}
.ky-switch[aria-disabled="true"] .ky-switch__track {
  border-style: dashed;
  border-color: rgba(var(--ky-rgb-bone), .24);
}
.ky-switch[aria-disabled="true"] .ky-switch__knob {
  background: rgba(var(--ky-rgb-bone), .34);
  box-shadow: none;
}

/* ---- The info popover -----------------------------------------------------
   PROGRESSIVE ENHANCEMENT, and the fallback is the design it replaces. Without
   the popover API the <div popover> is an unknown attribute, so the note simply
   renders in flow exactly as the old explanatory sentence did — and the button
   that would open it is hidden, because it would do nothing. With the API the
   note is display:none by UA default until opened, and the button appears.

   No JS: popovertarget gives light-dismiss, Esc, and focus handling for free.  */

.ky-info-btn { display: none; }

/* The decorative layers only exist where the popover does. In the fallback this
   element renders in flow as a plain note, and three absolutely-positioned
   backgrounds would have nothing sensible to position against. */
.ky-info-pop__edge,
.ky-info-pop__panel,
.ky-info-pop__bloom { display: none; }

.ky-info-pop {
  color: var(--ky-text-muted);
  font-family: var(--ky-font-tech);
  font-size: var(--ky-text-small);
  letter-spacing: normal;
  text-transform: none;
  max-width: 44ch;
}
.ky-info-pop p { margin: 0 0 var(--ky-space-3); max-width: none; }
.ky-info-pop p:last-child { margin-bottom: 0; }

@supports selector(:popover-open) {
  .ky-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    padding: 0;
    border-radius: var(--ky-radius-pill);
    border: 1px solid rgba(var(--ky-rgb-bone), .30);
    background: none;
    color: var(--ky-text-accent);
    font-family: var(--ky-font-mono);
    font-size: var(--ky-text-small);
    font-weight: var(--ky-weight-medium);
    line-height: 1;
    cursor: pointer;
    transition: background var(--ky-duration-fast) var(--ky-ease),
                border-color var(--ky-duration-fast) var(--ky-ease);
  }
  .ky-info-btn:hover {
    background: rgba(var(--ky-rgb-bone), .10);
    border-color: rgba(var(--ky-rgb-bone), .48);
  }

  /* A SCREEN, and now the same technical device as the rest of them: cut like
     machined plate, translucent over a real backdrop blur, carrying its own
     lattice and its own lit strands. Top-left is the deepest cut so the shape
     has a dominant corner rather than four equal nibbles; bottom-right is left
     SQUARE on purpose, being the corner nearest the button that opened it.

     THE CUTS ARE ON CHILD LAYERS, NOT ON THIS BOX. A popover is a focus
     destination, and clip-path clips an element's whole rendering INCLUDING its
     focus ring — a clipped control renders with no visible outline at any
     offset. So the box stays unclipped and three clipped layers sit behind the
     text, exactly as the masthead and the closing screen are built.            */
  .ky-info-pop[popover] {
    --ip-cut: 26px;      /* top-left, the dominant cut */
    --ip-cut-sm: 10px;   /* the two supporting cuts    */
    /* Padding AND the bloom's falloff runway, the same double duty
       --ky-panel-pad does — which is why it is larger than the text alone
       needs. Shrink it and the strands stop having room to fade before they
       reach a word. */
    --ip-pad: var(--ky-space-8);
    position: fixed;
    inset: auto var(--ky-gutter) calc(var(--mh-short) + var(--ky-space-6)) auto;
    margin: 0;
    isolation: isolate;
    width: min(44ch, calc(100vw - var(--ky-gutter) * 2));
    padding: var(--ip-pad);
    border: 0;
    background: none;
    filter: drop-shadow(0 24px 34px rgba(var(--ky-rgb-void), .85));
  }

  .ky-info-pop__inner { position: relative; z-index: 1; }

  /* One outline, three layers. ::edge is the lit rim, ::panel the glass inset by
     a hairline so the 1px that shows around it IS the rim — following every
     chamfer for free, which a border cannot do — and ::bloom the strands. */
  .ky-info-pop__edge,
  .ky-info-pop__panel,
  .ky-info-pop__bloom {
    /* Re-enables what the fallback rule above switches off. Miss this and the
       panel simply does not paint: the box has `background: none` by design
       (the glass lives on this layer), so the text ends up floating on nothing
       over whatever the popover happens to cover. */
    display: block;
    position: absolute;
    pointer-events: none;
    clip-path: polygon(
      var(--ip-cut) 0,
      calc(100% - var(--ip-cut-sm)) 0,
      100% var(--ip-cut-sm),
      100% 100%,
      var(--ip-cut-sm) 100%,
      0 calc(100% - var(--ip-cut-sm)),
      0 var(--ip-cut)
    );
  }
  .ky-info-pop__edge {
    inset: 0;
    background: linear-gradient(115deg,
      rgba(var(--ky-rgb-gridlit), .30) 0%,
      rgba(var(--ky-rgb-gridlit), .20) 40%,
      rgba(var(--ky-rgb-bone), .34) 78%,
      rgba(var(--ky-rgb-bone), .46) 100%);
  }

  /* TRANSLUCENT, unlike the closing screen — and here it earns its keep. This
     one FLOATS above the page rather than being set into it, so there is real
     backdrop to see: paper, lattice and the footer's own glass slide under it.
     That is the masthead's construction, and the alphas are the masthead's too.

     The binding case is paper, not glass. Over the cream ground this panel is
     the lightest it ever gets, so that is where the body copy is measured — see
     the figures in docs/STATUS.md. Lower these and re-measure over PAPER; the
     glass reading will flatter you. */
  .ky-info-pop__panel {
    inset: 1px;
    background:
      radial-gradient(120% 180% at 100% 0%, rgba(var(--ky-rgb-bone), .07), transparent 58%),
      repeating-linear-gradient(to right,  rgba(var(--ky-rgb-gridlit), .15) 0 1px, transparent 1px var(--ky-grid-major)),
      repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-gridlit), .15) 0 1px, transparent 1px var(--ky-grid-major)),
      linear-gradient(158deg, rgba(var(--ky-rgb-panel), .88) 0%, rgba(var(--ky-rgb-panel), .84) 46%, rgba(var(--ky-rgb-void), .86) 100%);
    background-position:
      0 0,
      var(--ky-lattice-x) var(--ky-lattice-y),
      var(--ky-lattice-x) var(--ky-lattice-y),
      0 0;
    background-repeat: no-repeat, repeat, repeat, no-repeat;
    /* One tile = one period — see .ky-paper. A popover's height is content-driven
       and will never be a whole number of periods. */
    background-size:
      auto,
      var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
      auto;
    -webkit-backdrop-filter: blur(14px) saturate(115%);
            backdrop-filter: blur(14px) saturate(115%);
  }

  /* The strands. This one IS .ky-panel's case — a small box that is entirely body
     copy — so it takes .ky-panel's apparatus unchanged: disc ∩ (H ∪ V), where the
     union of two one-dimensional edge ramps is opaque near the perimeter and zero
     over the text field. That rectangular soft-edged hole cannot be one gradient:
     an ellipse large enough to cover a text rect is larger than the panel, so it
     would erase the bloom entirely. */
  .ky-info-pop__bloom {
    inset: 1px;
    --gb-r: var(--ky-bloom);
    --gb-core: 1;
    --gb-halo: .34;
    --gb-glow: .16;
    --gb-fade: var(--ip-pad);
    mix-blend-mode: screen;
    -webkit-mask-image:
      radial-gradient(circle var(--gb-r) at var(--ky-bloom-x, -9999px) var(--ky-bloom-y, -9999px), #000 0%, rgba(0,0,0,.94) 38%, rgba(0,0,0,.52) 60%, rgba(0,0,0,.16) 76%, transparent 88%),
      linear-gradient(to right,  #000 0, transparent var(--gb-fade), transparent calc(100% - var(--gb-fade)), #000 100%),
      linear-gradient(to bottom, #000 0, transparent var(--gb-fade), transparent calc(100% - var(--gb-fade)), #000 100%);
            mask-image:
      radial-gradient(circle var(--gb-r) at var(--ky-bloom-x, -9999px) var(--ky-bloom-y, -9999px), #000 0%, rgba(0,0,0,.94) 38%, rgba(0,0,0,.52) 60%, rgba(0,0,0,.16) 76%, transparent 88%),
      linear-gradient(to right,  #000 0, transparent var(--gb-fade), transparent calc(100% - var(--gb-fade)), #000 100%),
      linear-gradient(to bottom, #000 0, transparent var(--gb-fade), transparent calc(100% - var(--gb-fade)), #000 100%);
    mask-composite: intersect, add, add;
    background-image:
      repeating-linear-gradient(to right,  rgba(var(--ky-rgb-lit), var(--gb-core)) 0 1px, transparent 1px var(--ky-grid-major)),
      repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-lit), var(--gb-core)) 0 1px, transparent 1px var(--ky-grid-major)),
      repeating-linear-gradient(to right,  rgba(var(--ky-rgb-fiber), var(--gb-halo)) 0 5px, transparent 5px var(--ky-grid-major)),
      repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-fiber), var(--gb-halo)) 0 5px, transparent 5px var(--ky-grid-major)),
      repeating-linear-gradient(to right,  rgba(var(--ky-rgb-fiber), var(--gb-glow)) 0 13px, transparent 13px var(--ky-grid-major)),
      repeating-linear-gradient(to bottom, rgba(var(--ky-rgb-fiber), var(--gb-glow)) 0 13px, transparent 13px var(--ky-grid-major));
    background-position:
      var(--ky-lattice-x) var(--ky-lattice-y),
      var(--ky-lattice-x) var(--ky-lattice-y),
      calc(var(--ky-lattice-x) - 2px) var(--ky-lattice-y),
      var(--ky-lattice-x) calc(var(--ky-lattice-y) - 2px),
      calc(var(--ky-lattice-x) - 6px) var(--ky-lattice-y),
      var(--ky-lattice-x) calc(var(--ky-lattice-y) - 6px);
    background-repeat: repeat, repeat, repeat, repeat, repeat, repeat;
    background-size:
      var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
      var(--ky-grid-major) 100%, 100% var(--ky-grid-major),
      var(--ky-grid-major) 100%, 100% var(--ky-grid-major);
    display: var(--ky-bloom-display, block);
  }

  /* The ring is on the unclipped box, so it survives — see above. */
  .ky-info-pop[popover]:focus-visible {
    outline: var(--ky-focus-width) solid var(--ky-focus);
    outline-offset: var(--ky-focus-offset);
  }
}

/* Focus sits OUTSIDE the control, never inset, in a hue outside both brand poles
   so focus never reads as accent or link. */
.ky-footer a:focus-visible,
.ky-switch:focus-visible,
.ky-info-btn:focus-visible {
  outline: var(--ky-focus-width) solid var(--ky-focus);
  outline-offset: var(--ky-focus-offset);
  border-radius: 2px;
}

/* ---- Narrow ---------------------------------------------------------------
   The meta row wraps rather than shrinks; the switch keeps its own line so the
   track never ends up beside a wrapped link with nothing to align to.         */
@media (max-width: 720px) {
  .ky-footer__base-wrap { gap: var(--ky-space-4); }
  .ky-fx { margin-inline-start: 0; width: 100%; }
}
