/* ==========================================================================
   footer.css — the brass plate.

   A chart is not signed on the chart. The certification goes on a plate
   screwed to the mount underneath it, and the survey sits recessed into that.
   That is the shape here — a full-bleed sheet of metal with the deep water
   inset into it, one bite taken out of its top edge where the plate was cut
   to clear the fitting, and the officer who certified it standing in it.

   The plate is drawn in the line colour — the same blue every contour on this
   page is ruled in, which is why it can run the whole way round without
   reading as a fifth colour someone brought in for the foot of the page. It
   is kept to the width of a margin and no more: the frame's job is to close
   the shape, not to be looked at. The brass is spent inside the recess, where
   an accent is an accent.

   The border used to be a rule drawn on the panel. It is not a rule any more:
   the frame is a different material from the thing it holds, which is the only
   kind of frame that survives a dark mode where the page and the panel are
   within a few percent of each other.
   ========================================================================== */

.main-footer {
  position: relative;
  z-index: 1;
  margin-block-start: var(--spacing-xxl);
  /* The inset is padding on the plate, not margin on the recess: as a margin
     it collapsed straight out through the footer and the plate showed no metal
     above the top edge at all. */
  padding: var(--plate-inset) var(--plate-inset) var(--plate-overhang);
  border-top: 0;
  border-radius: var(--plate-corner);
  font-size: 0.9rem;

  /* Fixed colours, and the same under either theme: the water this plate
     holds is the same water at midnight as at noon, and the paper around it
     is the same paper. The foot of the page is the one place that does not
     change when the page does — but the values are the page's own, lifted
     off variables.css (--primary-light over --text-primary) rather than
     invented here, so it is the same paper and not merely a pale one. */
  --foam: #f4f9fc;
  --brass: #e9b44c;
  /* The frame is the page: the reference footer has no drawn band round the
     panel, the page's own ground shows through the inset and the cuts. */
  --ground: var(--bg-primary);
  --ground-ink: var(--text-primary);
  --sea-abyss: #06192c;
  --sea-line: #2e86c0;
  --foot-body: #a9c6dd;

  /* Every measurement of the plate comes off these two, so the bite, the
     corners and the inset can never drift apart. */
  /* Measured off the reference at 2000px wide: 23px of ground round the
     panel, a 40px panel corner, pockets 35px above the bottom band. */
  --plate-inset: clamp(10px, 1.15vw, 24px);
  --plate-round: clamp(18px, 2vw, 40px);
  --bite-h: clamp(22px, 1.75vw, 36px);
  /* The pocket's edge is an S: a convex turn at its top and a concave one at
     its foot, each half the pocket's height tall and about twice as wide. */
  --shoulder: calc(var(--bite-h) * 1.05);
  --shoulder-h: calc(var(--bite-h) * 0.5);

  /* The officer's own size, and therefore the width of everything that has to
     get out of his way: the gap between the pockets and the gap between the
     two crew columns are both this, so neither can drift into him when the
     window changes. 0.87 is his picture's aspect ratio — the one number here
     that comes off the file rather than off the plate. */
  --portrait-h: clamp(230px, 31vw, 430px);
  --portrait-clear: clamp(18px, 3.2vw, 56px);
  --officer-gap: calc(0.87 * var(--portrait-h) + 2 * var(--portrait-clear));

  /* A pocket is a holder for one short line, so it is the width of that line
     and its margin — not of everything between the corner and the officer.
     Run out to meet him and the line sits in a metre of empty metal, which is
     what the plate looked like when this was measured off him instead. The
     cap is still his clearance, so a narrow window closes the pocket before
     it can reach him. */
  --pocket-w: min(clamp(200px, 15vw, 320px),
                  calc((100% - var(--officer-gap)) / 2 + var(--plate-inset)));
  /* How far the officer stands out of the recess, and therefore how much
     plate has to be left under it for him to stand on. */
  --plate-overhang: var(--plate-inset);
  /* The size of the square each small fillet is cut from. */
  --fillet: calc(var(--notch-h) * 0.5);
  /* The plate's own corners, turned so the band around the recess keeps one
     width the whole way round: the outer arc is the inner arc plus the metal
     between them, which is the only relationship that reads as a band and not
     as two unrelated curves. */
  --plate-corner: calc(var(--plate-round) + var(--plate-inset));

  /* How far the tab rises above the panel. Its width comes off this too
     (see .frame-bite-top), so the shape is the same shape at every size.

     The pen hangs up and to the right of the point it writes with, so the
     signature is set low enough for the nib to stay inside this depth rather
     than the depth being cut to suit the pen. */
  --notch-h: clamp(26px, 2.6vw, 52px);

  background: var(--ground);
  color: var(--ground-ink);
}

/* After dark the page is deep water too, and a panel of deep water on it
   would be the same water as the page. So at night the panel's outline is
   drawn: one hairline, one stroke, run round the whole shape — the tab, its
   shoulders, the pockets and their feet included. It is a single SVG path,
   written by js/footer-outline.js from the same measurements the pieces below
   are cut to, because a line assembled from the edges of a dozen pieces is a
   dozen lines, and shows it at every join. By day the stroke is transparent. */
.main-footer { --hair: transparent; }

.frame-outline {
  position: absolute;
  z-index: 3; /* over the cut pieces, under the officer, who stands through it */
  overflow: visible;
  pointer-events: none;
}

.frame-outline path {
  fill: none;
  stroke: var(--hair);
  stroke-width: 1;
  stroke-linejoin: round;
}

[data-theme="dark"] .main-footer {
  /* Opaque, not translucent: where two pieces of the line overlap by a pixel a
     translucent line doubles up and brightens. */
  --hair: #c6ccd3;
}

/* The lift.

   Moving the sentence up *inside* the recess is not available: the signature
   and the pen that writes it live in the gap between the sentence and the top
   edge, and closing that gap is what sent the pen out through the cut. So the
   block does not move inside the plate — the plate moves. The footer starts
   higher on the page by --lift and grows by the same --lift further down, so
   its bottom edge, the officer, the instrument row and the stamped line all
   stay exactly where they were, and every distance inside the recess is the
   one the notch and the pen were measured against.

   The other half of this is under .footer-crew, where the height goes back
   in — it has to be written after that rule rather than here, because two
   rules of the same weight are settled by which one comes last.

   Desk only: on a phone the footer already sits close to what is above it. */
@media (min-width: 721px) {
  .main-footer {
    --lift: 56px;
    margin-block-start: calc(var(--spacing-xxl) - var(--lift));
  }
}

/* ------------------------------------------------------------- the recess */

.footer-frame {
  position: relative;
  /* No overflow clip: the officer stands *through* the bottom edge, which is
     the whole reason he is here rather than in a box of his own. The tile
     below carries the rounding instead. */
  border-radius: var(--plate-round);
}

/* The water, as a layer and not as the frame's background. The frame cannot
   be clipped — the officer is its child and stands through its bottom edge —
   and the flood that fills the recess when the footer is reached is a clip.
   So the water is a childless element the clip can run on, and the frame
   itself is glass: the plate shows through it until the water arrives. */
.frame-water {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  background-color: var(--sea-abyss);
  pointer-events: none;
}

/* The chart printed on the water. Its own run of the tile, not a continuation
   of the page's — this is a different sheet. */
.footer-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1; /* over the water, which is a child and paints after a ::before */
  border-radius: inherit;
  background-color: var(--sea-line);
  -webkit-mask-image: var(--chart-mask);
  mask-image: var(--chart-mask);
  -webkit-mask-size: var(--chart-tile, 560px);
  mask-size: var(--chart-tile, 560px);
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
  opacity: 0.5;
  pointer-events: none;
}

/* The chart does not stop at the notch: it is printed on the tab too, as the
   tab's own ::before (below). The band that used to carry it up here was a
   second copy of the tab's shape, and two copies of a shape drift apart. */

/* The bites
   ------------------------------------------------------------------------
   Three cuts in the plate: one hanging down from the top edge, two rising at
   the bottom corners where the engraved line is stamped. Each is a tongue of
   plate with the corners where it meets the edge turned the other way —
   concave, so the metal reads as one continuous sheet the recess was milled
   out of rather than as a dark box with tabs parked on it. Convex corners
   alone make a sticker; it is the inside corners that make it a cut.

   Every fillet is a square of plate with a quarter disc removed by a radial
   mask, so the curve is exact at any radius and costs no markup. And because
   the ground *is* plate, a bite never has to be clipped to anything: brass
   over brass is brass.

   Physical left and right, not logical: these are corners of a picture. */
.frame-bite {
  position: absolute;
  z-index: 2;
  background: var(--ground);
  pointer-events: none;
}

.frame-bite::before,
.frame-bite::after {
  content: "";
  position: absolute;
  width: var(--fillet);
  height: var(--fillet);
  background: var(--ground);
}

/* The tongue at the top edge.

   It rises, it does not hang: the recess pushes *up* into the plate rather
   than the plate dipping down into the recess.

   Its edge is one S each side — a concave turn off the panel's top edge, a
   straight run at about 45 degrees, and a convex turn onto the top — with no
   vertical wall anywhere. That profile is the reference footer's, measured
   off it and redrawn: each shoulder is about two and a half times as wide as
   the tab is tall, and the top is four and a quarter times. It was a box
   with rounded corners and a small flare at the foot before, and the eye
   reads a box as a box however soft its corners are; the long pull is what
   makes it read as the panel's own edge rising.

   The shape is one drawing, used as a mask, so it scales with --notch-h and
   nothing else: every curve keeps its proportion at every size. The element
   is a full-width band, so the chart printed on it (::before) is tiled from
   the frame's own left edge and meets the chart below without a seam; the
   mask is what keeps only the tab of it. It reaches 4% of its height down
   into the panel, so the join is water over water, never an edge on an edge.

   The flat top is centred, not the base: the right shoulder is a little
   longer than the left, as in the reference, and it is the top the eye
   centres. js/footer-outline.js draws the night outline on the same numbers. */
.frame-bite-top {
  --notch-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 955.33 104'%3E%3Cpath d='M0 100C55.49 100 108.88 78.75 149.2 40.62C176.79 14.54 213.32 0 251.29 0H677.91C719.07 0 758.93 14.37 790.62 40.62C836.92 79 895.2 100 955.33 100V104H0Z'/%3E%3C/svg%3E");
  bottom: calc(100% - var(--notch-h) * 0.04);
  left: 0;
  right: 0;
  height: calc(var(--notch-h) * 1.04);
  background: var(--sea-abyss);
  -webkit-mask: var(--notch-mask) calc(50% + var(--notch-h) * 0.131) 0 / auto 100% no-repeat;
  mask: var(--notch-mask) calc(50% + var(--notch-h) * 0.131) 0 / auto 100% no-repeat;
}

/* The chart, carried up onto the tab: the same tile, counted from the same
   corner as the panel's (the band is as wide as the frame and sits the
   tab's height above it, so it is shifted down by exactly that). */
.frame-bite-top::before {
  inset: 0;
  width: auto;
  height: auto;
  background-color: var(--sea-line);
  -webkit-mask-image: var(--chart-mask);
  mask-image: var(--chart-mask);
  -webkit-mask-size: var(--chart-tile, 560px);
  mask-size: var(--chart-tile, 560px);
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
  -webkit-mask-position: 0 calc(var(--notch-h) * 1.04);
  mask-position: 0 calc(var(--notch-h) * 1.04);
  opacity: 0.5;
}

.frame-bite-top::after { content: none; }

/* the two pockets the engraved line sits in. One fillet each: the outer side
   of a corner bite is already plate, so there is no corner to turn there. */
.frame-bite-bl,
.frame-bite-br {
  /* Down to the plate's own bottom edge rather than the recess's, so the
     pocket and the plate turn the same corner with the same radius. Stopping
     short left a square nub of metal sitting outside the rounded corner —
     invisible while the two were the same square shape, and not once the
     plate had corners. */
  bottom: calc(-1 * var(--plate-overhang));
  height: calc(var(--bite-h) + var(--plate-overhang));
  /* The two pockets are the bottom edge, and the recess comes down between
     them to let the officer stand through it. */
  width: var(--pocket-w);
}

/* The inner corner turns convex, the foot below it turns concave: an S, the
   same edge the tab at the top has, upside down. */
.frame-bite-bl {
  left: -1px;
  border-top-right-radius: var(--shoulder) var(--shoulder-h);
}

.frame-bite-br {
  right: -1px;
  border-top-left-radius: var(--shoulder) var(--shoulder-h);
}

/* The foot
   ------------------------------------------------------------------------
   Where the pocket's wall reaches the bottom band the panel's corner is
   rounded off — a quarter ellipse of ground kept outside the panel's arc, so
   the pocket widens into the band the way the tab widens into the panel. */
.frame-bite-bl::before,
.frame-bite-br::before {
  bottom: var(--plate-overhang);
  width: calc(var(--shoulder) + 1px);
  height: var(--shoulder-h);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.frame-bite-bl::before {
  left: calc(100% - 1px);
  -webkit-mask-image: radial-gradient(var(--shoulder) var(--shoulder-h) at 100% 0,
    transparent 98%, #000 100%);
  mask-image: radial-gradient(var(--shoulder) var(--shoulder-h) at 100% 0,
    transparent 98%, #000 100%);
}

.frame-bite-br::before {
  right: calc(100% - 1px);
  -webkit-mask-image: radial-gradient(var(--shoulder) var(--shoulder-h) at 0 0,
    transparent 98%, #000 100%);
  mask-image: radial-gradient(var(--shoulder) var(--shoulder-h) at 0 0,
    transparent 98%, #000 100%);
}

/* The panel's own bottom corners, above the pockets: a square of ground at the
   pocket's outer top corner with the quarter disc the panel keeps taken out
   of it, so the panel turns the same radius here as at its top corners. */
.frame-bite-bl::after,
.frame-bite-br::after {
  bottom: calc(100% - 1px);
  /* One pixel wider than the radius, out over the panel's edge, so the square
     also covers the straight hairline for the height of the arc. */
  width: calc(var(--plate-round) + 1px);
  height: var(--plate-round);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.frame-bite-bl::after {
  left: -1px;
  -webkit-mask-image: radial-gradient(circle at 100% 0,
    transparent calc(var(--plate-round) - 1px), #000 var(--plate-round));
  mask-image: radial-gradient(circle at 100% 0,
    transparent calc(var(--plate-round) - 1px), #000 var(--plate-round));
}

.frame-bite-br::after {
  right: -1px;
  -webkit-mask-image: radial-gradient(circle at 0 0,
    transparent calc(var(--plate-round) - 1px), #000 var(--plate-round));
  mask-image: radial-gradient(circle at 0 0,
    transparent calc(var(--plate-round) - 1px), #000 var(--plate-round));
}

/* A phone gets the same two pockets, not a band under the plate: dropping
   them left both lines stacked in open metal below the recess, which is a lot
   of empty plate for eighteen words. Here the pocket is sized to its line
   directly — the desk's cap is measured off the officer, and on a narrow
   window that cap closes to nothing. He crosses them instead, and the fade
   below takes care of the join. */
@media (max-width: 720px) {
  .frame-bite-bl,
  .frame-bite-br { --pocket-w: clamp(145px, 44vw, 195px); }
}

/* ------------------------------------------------------------- the recess */

.footer-plate {
  position: relative;
  z-index: 3;
  /* Only the top of the clamp moves: on a phone this is already at its floor,
     so the lift lands on the desk and leaves the phone where it was. */
  /* Room at the top for the autograph's ascenders and the pen above them —
     less the rise, which is handed back under the sentence (.footer-crew). */
  padding-block: calc(clamp(6rem, 11vw, 9.5rem) - var(--decl-rise)) 0;
  --plate-pad: clamp(1.25rem, 4vw, 3.25rem);
}

/* The rise: how far the statement is lifted inside the plate, clear of the
   peak of the officer's cap. Taken off the room above it and given back
   below it, so the footer is exactly as tall as it was and nothing under the
   sentence moves. The ceiling is the pen — at its highest, riding up off the
   loop of the D, it has to stay inside the panel and its tab — and that
   headroom is not one size: it is widest on a laptop, thinnest on a tablet
   (where the autograph is largest against the room it has), and shrinks
   again on a wide screen, where the autograph keeps growing after the room
   above it has stopped. Each step keeps about ten pixels of it spare
   (measured frame by frame through the whole signing). */
.main-footer { --decl-rise: clamp(16px, 160px - 7.3vw, 40px); }

@media (max-width: 991px) {
  .main-footer { --decl-rise: 12px; }
}

@media (max-width: 767px) {
  .main-footer { --decl-rise: 24px; }
}

@media (max-width: 479px) {
  .main-footer { --decl-rise: clamp(8px, 118px - 22.6vw, 24px); }
}

/* ------------------------------------------------------- the certification

   Centred, because a certification is signed across the middle of the plate
   and not tucked into a corner, and because the officer stands directly under
   it — the statement and the man making it are one column. */
.footer-sign {
  position: relative;
  direction: ltr;
  max-width: min(1180px, 94%);
  margin-inline: auto;
  padding-inline: var(--plate-pad);
  text-align: center;
  /* The statement's size, here rather than on the line itself, because the
     signature is placed against it: how far the ink comes down into the
     first line is a share of that line, whatever size it is set at. */
  --decl-size: clamp(1.75rem, 4.9vw, 4.2rem);
}

/* Above the words, touching them. Set down across the first line it was
   written over the statement and crossed it out; now the autograph stands
   on the line and only the tail of it comes down across the tops of the
   capitals, the way a signature is put to the head of what it signs.
   The bottom of the ink sits a quarter of the way into the first line: the
   box is lowered by that much of the statement's size, less the sliver of
   box under the ink (11 of the 292 viewBox units, a fiftieth of the width). */
.footer-signature {
  position: absolute;
  z-index: 2;
  /* Physical, not logical: a picture does not flip with the text. */
  bottom: calc(100% - var(--decl-size) * 0.26 - var(--sig-w) * 0.02);
  /* Centred on the statement, not hung off its middle: the offset was moving
     the box's left edge to the centre line rather than the box itself. */
  left: 50%;
  /* Sized off landonorris.com's footer autograph and then a fifth larger:
     theirs is a 13.0625rem box on a rem that scales with the viewport
     (clamp(992px, 100vw, 1920px) / 1728 * 16), and their ink fills it. Ours
     fills 420 of the 546 viewBox units, so the box is widened by that much
     to put the same width of ink on the page, and then by 1.2. The narrower
     screens below follow their breakpoints the same way. */
  --sig-w: clamp(184px, 18.55vw, 356px);
  width: var(--sig-w);
  height: auto;
  transform: translateX(-50%);
  overflow: visible;
  pointer-events: none;
  /* Slow enough that somebody who was not looking for it still catches it
     happening. At two seconds it was over before you had found it. The
     script reads this: it is the time the whole autograph takes, lifts
     included. */
  --sign-dur: 5.2s;
}

/* Their tablet and phone steps: the rem is worked out on 20px of design
   under 992px, and on 48px with a 10rem box under 480px. */
@media (max-width: 991px) {
  .footer-signature { --sig-w: clamp(178px, 23.18vw, 230px); }
}

@media (max-width: 767px) {
  .footer-signature { --sig-w: clamp(112px, 23.18vw, 178px); }
}

@media (max-width: 479px) {
  .footer-signature { --sig-w: clamp(136px, 42.6vw, 204px); }
}

/* The ink is one filled shape, not a stroked line: marker ink with its own
   weight — tapered in where the pen lands, flicked out to nothing where it
   leaves, a little heavier on the down-strokes, round at every turn
   (tools/make-signature.py). A constant stroke reads as a wire bent into
   the shape of a name. */
.footer-signature .signature-ink {
  fill: var(--brass);
  /* Lit from within, the way a neon autograph is: a soft brass halo that
     appears with the ink because it is cast by the ink. */
  filter: drop-shadow(0 0 4px rgba(233, 180, 76, 0.5))
          drop-shadow(0 0 13px rgba(233, 180, 76, 0.26));
}

/* Nothing in this sheet hides the ink: with the script never running the mask
   is fully painted and the autograph is simply there. When the script does
   run it sets each trail stroke's dash to that stroke's own length, pushes it
   off the end, and pulls it back on exactly as far as the nib has travelled,
   frame by frame. There is no timed animation here — a stroke on a stylesheet
   timer and a pen on another are two things that only agree while nothing is
   under load. */

/* The pen
   ------------------------------------------------------------------------
   Built along one axis from the nib at its origin: a gold nib with its slit
   and breather hole, a steel section, a brass collar and a pearl barrel with
   a clip. Every gradient runs across the barrel rather than down it, because
   the thing that makes a cylinder read as a cylinder is the shading around
   it, not along it.

   The stroke on its own is a line appearing, and a line appearing is an
   effect. What makes it read as *writing* is the thing laying it down: the
   nib is placed on each frame at the very point the ink has reached, so the
   ink only ever exists behind it. Between strokes it lifts, crosses, and
   comes down again — a pen does not teleport.

   Both the position and the ink are written by js/catalog.js. */
.footer-nib {
  opacity: 0;
  transition: opacity 0.3s linear;
}

.footer-frame.is-signing .footer-nib,
.footer-frame.is-signed .footer-nib {
  opacity: 1;
}

/* Cast onto the plate, offset the way the light is: it is what stops the pen
   reading as a sticker laid on the panel. */
.pen-shadow {
  fill: rgba(3, 12, 22, 0.42);
  transform: translate(5px, 7px);
  filter: blur(3px);
}

.pen-barrel  { fill: url(#penBarrel); }
.pen-section { fill: url(#penSection); }
.pen-collar  { fill: url(#penTrim); }
.pen-finial  { fill: url(#penTrim); }
.pen-clip    { fill: url(#penTrim); }
.pen-nib     { fill: url(#penNib); }
/* The lit facet of the nib and the length of highlight down the barrel: the
   two places a real pen catches the room. */
.pen-facet   { fill: rgba(255, 246, 220, 0.55); }
.pen-gloss   { fill: rgba(255, 255, 255, 0.72); }
/* The slit runs from the tip to the breather hole, and both are cut *into*
   the nib, so they are the plate showing through rather than a drawn line. */
.pen-slit {
  fill: none;
  stroke: #7a5514;
  stroke-width: 1.1;
  stroke-linecap: round;
}
.pen-hole { fill: #7a5514; }

/* Nobody is watching it being written, so it is simply written — and there is
   nothing for a pen to be doing in a still picture. */
@media (prefers-reduced-motion: reduce) {
  .footer-nib {
    display: none;
  }
}

/* The footer's two faces
   ------------------------------------------------------------------------
   Mona Sans (GitHub's, OFL) is the grotesk the reference footer is set in,
   and it is set here the way it is set there: heavy, a little narrowed,
   capitals, and packed to a line-height of one — the links a stack of
   blocks, not a list with air between the rows.

   The statement is the one thing here that is not the reference's. It is
   the officer's, so it is lettered the way the navy letters: Stardos Stencil,
   a bold serif cut as a stencil — the face of a motto painted on a bulkhead
   or across the lid of a crate, formal enough for dress whites and blunt
   enough for what the sentence says. Both faces are Latin only; nothing in
   this footer that uses them is Arabic. */
.main-footer {
  --foot-sans: 'Mona Sans', 'Helvetica Neue', Arial, sans-serif;
  --foot-statement: 'Stardos Stencil', 'Rockwell', Georgia, serif;
}

/* The declaration
   ------------------------------------------------------------------------
   One face, split where the sentence turns: what he will do in foam, what it
   would take to stop him in brass, each on a line of its own. The change is
   the sentence's own hinge, not a decoration laid over it — and with one
   stencil across both halves the line reads as a single order stamped out,
   not two voices. */
.footer-declaration {
  margin: 0;
}

.footer-declaration p {
  margin: 0;
  color: var(--foam);
  direction: ltr;
  font-family: var(--foot-statement);
  font-size: var(--decl-size);
  font-weight: 700;
  /* Capitals only, so there is nothing below the baseline to make room for;
     just enough leading that the stencil bridges of one line do not read as
     joined to the serifs of the next. Light on dark, so a hair of tracking. */
  line-height: 0.94;
  letter-spacing: 0.01em;
  text-align: center;
  text-transform: uppercase;
  text-wrap: balance;
}

.footer-declaration em,
.decl-em {
  color: var(--brass);
  font-style: normal;
}

/* The turn in the sentence is a turn in the line: the serif half starts a
   line of its own, so the break falls where the meaning does and never
   leaves a brass "A" hanging off the end of the white. Only once the half
   has letters in it — while the first half is being typed the caret must
   stay on the first line. */
.footer-declaration em::before,
.decl-em:not(:empty)::before {
  content: "\A";
  white-space: pre;
}

/* The stop at the end of it. Not a full stop — the mark this site is drawn
   with, in the ink the rest of the page is ruled in. */
.footer-declaration p::after {
  content: "";
  display: inline-block;
  width: 0.34em;
  height: 0.34em;
  margin-inline-start: 0.24em;
  vertical-align: 0.06em;
  background: var(--brass);
  transform: rotate(45deg);
}

/* ------------------------------------------------------------ coming aboard

   The hero arrives once, on load. The foot of the page is reached by
   scrolling, so it arrives when it is reached — and it arrives the way the
   thing it depicts is made. A plate is set down. The recess is milled out of
   it and floods: one brass line — the survey — runs down the plate top to
   bottom, and above the line there is water, chart, and print; below it there
   is still bare metal. The officer surfaces through the bottom edge once the
   water is in. The two lines in the pockets are stamped into the metal last.
   Then, and only then, the statement is keyed in and signed (js/catalog.js
   waits for all of this before it starts typing).

   One movement, not six fades: the survey line is what everything else is
   timed against, and nothing here appears without the line having passed it.

   Armed from script, not from the stylesheet. A block that hides itself and
   waits to be told otherwise is a block that is simply missing when the
   script fails; this way the footer is whole to begin with and only ever
   hidden by something that is already running and will finish the job. */
.main-footer {
  /* The whole sequence hangs off these three moments. */
  --set-dur: 560ms;
  --survey-delay: 300ms;
  --survey-dur: 1150ms;
  --survey-end: calc(var(--survey-delay) + var(--survey-dur));
  /* Linear in the middle so the line travels at one speed down the plate —
     a survey is paid out, not thrown. */
  --survey-ease: cubic-bezier(0.55, 0.05, 0.35, 1);
}

.main-footer.is-boarding {
  opacity: 0;
}

.main-footer.is-boarding .footer-portrait,
.main-footer.is-boarding .footer-baseline {
  opacity: 0;
}

/* Nothing where the tab will be: the tab and the run of chart printed on it
   arrive together, whole, when the water reaches the top edge. Changing the
   tab's colour instead left its shoulders and its pattern showing on their
   own for a moment, and the tab read as a separate piece. */
.main-footer.is-boarding .frame-bite-top,
.main-footer.is-boarding .frame-bite-bottom,
.main-footer.is-boarding .frame-outline {
  opacity: 0;
}

.main-footer.is-boarding .frame-water,
.main-footer.is-boarding .footer-frame::before,
.main-footer.is-boarding .footer-plate {
  clip-path: inset(0 0 100% 0);
}

/* 1. The plate is set down. */
@keyframes plate-set {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: none; }
}

.main-footer.is-aboard {
  animation: plate-set var(--set-dur) var(--ease-out) both;
}

/* 2. The survey. One clip, run on three layers at once — the water, the chart
   printed on it, and everything printed over that — so the line reveals a
   finished plate and not three things arriving separately. `backwards`, not
   `both`, on the print: the end state is a clip of nothing, and leaving even
   that on the plate would cut the signature where it rises into the notch. */
@keyframes survey-reveal {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0 0); }
}

.main-footer.is-aboard .frame-water,
.main-footer.is-aboard .footer-frame::before,
.main-footer.is-aboard .footer-plate {
  /* The base the animation returns to. is-boarding stays on the footer, and
     its clip-of-nothing is what these would fall back to otherwise — which is
     a plate that empties itself the moment the line reaches the bottom. */
  clip-path: none;
  animation: survey-reveal var(--survey-dur) var(--survey-ease) var(--survey-delay) backwards;
}

@keyframes tongue-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.main-footer.is-aboard .frame-bite-top,
.main-footer.is-aboard .frame-bite-bottom,
.main-footer.is-aboard .frame-outline {
  opacity: 1;
  animation: tongue-in 220ms linear var(--survey-delay) backwards;
}

/* The line itself: brass, lit, the width of the printed area. It runs the
   height of the recess at exactly the speed the clip opens, and is
   extinguished at the bottom. */
.frame-sweep {
  position: absolute;
  z-index: 6;
  top: 0;
  right: var(--plate-pad, 2rem);
  left: var(--plate-pad, 2rem);
  height: 2px;
  background: var(--brass);
  box-shadow:
    0 0 0 1px rgba(233, 180, 76, 0.35),
    0 0 14px 3px rgba(233, 180, 76, 0.55),
    0 6px 28px 6px rgba(233, 180, 76, 0.25);
  opacity: 0;
  pointer-events: none;
}

@keyframes survey-run {
  from { top: 0; opacity: 1; }
  to   { top: 100%; opacity: 1; }
}

@keyframes survey-out {
  to { opacity: 0; }
}

.main-footer.is-aboard .frame-sweep {
  animation:
    survey-run var(--survey-dur) var(--survey-ease) var(--survey-delay) both,
    survey-out 320ms ease-out var(--survey-end) forwards;
}

/* 3. The officer surfaces through the bottom edge once there is water to
   surface from. His own keyframe, and it has to be his own: he is centred with
   a transform, and a rise that animated `transform` alone would drop the
   centring for the length of the animation and leave him standing half a body
   to one side. */
@keyframes officer-surface {
  from { opacity: 0; transform: translate(-50%, 11%); }
  to   { opacity: 1; transform: translateX(-50%); }
}

.main-footer.is-aboard .footer-portrait {
  animation: officer-surface 900ms var(--ease-out) calc(var(--survey-end) - 250ms) both;
}

/* 4. Stamped into the metal: the two engraved lines land the way every stamp
   on this site lands, a touch large and settling onto the plate. */
@keyframes stamp-in {
  from { opacity: 0; transform: scale(1.06); }
  to   { opacity: 1; transform: none; }
}

/* Stamped one line at a time, not the row: the row runs the full width of
   the page, and scaled up six percent while it waits its turn it hung past
   both edges and gave the page a horizontal scrollbar — a strip of nothing
   under the footer that the page could be scrolled on to. */
.main-footer.is-aboard .footer-baseline { opacity: 1; }

.main-footer.is-aboard .footer-baseline > * {
  animation: stamp-in 420ms var(--ease-stamp) calc(var(--survey-end) + 150ms) both;
}

/* ------------------------------------------------------------- the keying in

   The statement is typed out, and typed badly: the letters of a word come out
   in the wrong order, the mistake is noticed a beat late, and it is picked
   back off one letter at a time. That is the whole point of doing it this way
   rather than fading the sentence in — a line about not being beaten, put down
   by somebody who cannot get through it clean and does not stop, says more
   about the person signing it than the sentence does.

   Every mistake here is the same kind, two letters swapped. One kind of error,
   made a couple of times, reads as a hand going too fast. Three different kinds
   reads as a script.

   The text is written and erased in js/catalog.js; everything below is what
   it is written with. */
.decl-caret {
  display: inline-block;
  width: 0.5em;
  height: 0.82em;
  margin-inline-start: 0.06em;
  vertical-align: -0.04em;
  background: var(--brass);
}

/* Blinking is what a caret does when nothing is being typed into it. While
   the keys are going it holds solid, so the blink is not competing with the
   letters for the eye. */
.decl-caret.is-idle {
  animation: caret-blink 1.06s steps(1, end) infinite;
}

@keyframes caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

.decl-plain { color: var(--foam); }
.decl-em { color: var(--brass); }

/* ------------------------------------------------------------- the last run

   The line comes off and goes back down at a speed nobody was typing at, and
   the plate lights up for it. While the wipe is on the caret is bright; while
   the dash is on the brass burns and the caret throws light onto the letters
   arriving under it; and the moment it lands the whole line flares once and
   settles — a struck match, not a lamp left on. Nothing here stays lit: the
   finished sentence looks exactly as it would have if it had been typed
   cleanly the first time, which is the point. */
.footer-declaration p.is-wiping .decl-caret,
.footer-declaration p.is-dashing .decl-caret {
  background: #fff7dc;
  box-shadow:
    0 0 6px rgba(255, 236, 170, 0.95),
    0 0 18px rgba(214, 168, 74, 0.75);
}

.footer-declaration p.is-dashing .decl-em {
  color: #ffe8a8;
  text-shadow:
    0 0 10px rgba(255, 222, 140, 0.85),
    0 0 26px rgba(214, 168, 74, 0.55);
}

.footer-declaration p.is-dashing .decl-plain {
  color: #fff;
  text-shadow: 0 0 12px rgba(255, 255, 255, 0.45);
}

.footer-declaration p.is-landed .decl-ink {
  animation: decl-land 1100ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.footer-declaration p.is-landed .decl-em {
  animation: decl-land-brass 1100ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

@keyframes decl-land {
  0%   { transform: translateY(-1px) scale(1.012); filter: brightness(1.55); }
  35%  { transform: none; filter: brightness(1.18); }
  100% { transform: none; filter: brightness(1); }
}

@keyframes decl-land-brass {
  0% {
    text-shadow:
      0 0 14px rgba(255, 226, 150, 1),
      0 0 40px rgba(214, 168, 74, 0.85);
  }
  100% { text-shadow: 0 0 0 rgba(214, 168, 74, 0); }
}

/* The sentence as it is read out: whole, settled, and never the misspelt
   version. It is the only copy in the accessibility tree — the letters going
   down beside it are marked away from it. */
.decl-said {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The stop, once the line is being keyed in. It is the same mark the stylesheet
   prints on `p::after` for a page whose script never ran — moved into the line
   so that it can be typed with the sentence and so the caret can come to rest
   after it rather than on top of it. Two of them would be one too many, so the
   printed one stands down for as long as the typed one exists. */
.footer-declaration p.is-keying::after {
  content: none;
}

.decl-stop {
  display: none;
  width: 0.34em;
  height: 0.34em;
  margin-inline-start: 0.24em;
  vertical-align: 0.06em;
  background: var(--brass);
  transform: rotate(45deg);
}

/* Out of the flow until it is typed, not merely invisible: the caret is what
   marks where the next letter goes, and a stop already holding its place would
   have held the caret half a character clear of the word the whole way down
   the line. It arrives with the last keystroke and moves the caret along by
   its own width, which is what typing a full stop does. */
.footer-declaration p.is-typed .decl-ink .decl-stop {
  display: inline-block;
}

/* The placeholder is measured on the finished line, so it carries the stop and
   the caret from the start — it is invisible either way, and a copy that grew
   by half a character at the last keystroke could rewrap on it. */
.decl-ghost .decl-stop {
  display: inline-block;
}

/* ------------------------------------------------------------ the correction

   The aside somebody mutters while they are picking the mistake back out. It
   is pinned under the caret, at the letter being erased, because that is what
   it is about — anywhere else on the plate it would be a caption on the whole
   sentence rather than a remark about one word.

   Set in the Arabic body face at the size of a margin note, on a slip of the
   same brass the sentence's second half is printed in, and knocked a degree or
   two off square: it is the one thing in this footer that is not part of the
   record, and it should not look like it was set with the rest of it. */
/* The line is its own positioning context: the letters are painted over the
   invisible copy that holds the block open, and the aside is pinned to the
   caret inside it. */
.footer-declaration p.is-keying {
  position: relative;
}

.decl-ghost {
  visibility: hidden;
}

.decl-ink {
  position: absolute;
  inset: 0;
}

.decl-aside {
  position: absolute;
  z-index: 3;
  /* Physical, and it has to be: an absolutely positioned box resolves its own
     logical insets against its own direction, and this slip is set in Arabic.
     `inset-inline-start` on it means the right edge, and the measurement it is
     given was taken in the sentence's left-to-right box. */
  top: 0;
  left: 0;
  padding: 0.3rem 0.7rem;
  color: var(--ground-ink);
  background: var(--brass);
  border-radius: 999px;
  direction: rtl;
  font-family: var(--font-body);
  font-size: clamp(0.72rem, 1.5vw, 0.9rem);
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
  text-transform: none;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 6px) rotate(-2.5deg) scale(0.94);
  transform-origin: 50% 0;
  transition: opacity 260ms var(--ease-out), transform 340ms var(--ease-out);
}

.decl-aside.is-said {
  opacity: 1;
  transform: translate(-50%, 0) rotate(-2.5deg) scale(1);
}

/* The stillness version. The sentence is simply there, the plate is simply
   there, and nobody is being shown a mistake being made — the aside has
   nothing to be an aside to. */
@media (prefers-reduced-motion: reduce) {
  .main-footer.is-boarding,
  .main-footer.is-boarding .footer-portrait,
  .main-footer.is-boarding .footer-enquiry,
  .main-footer.is-boarding .footer-baseline {
    opacity: 1;
  }

  .main-footer.is-boarding .frame-water,
  .main-footer.is-boarding .footer-frame::before,
  .main-footer.is-boarding .footer-plate {
    clip-path: none;
  }

  .main-footer.is-boarding .frame-bite-top,
  .main-footer.is-boarding .frame-bite-bottom,
  .main-footer.is-boarding .frame-outline { opacity: 1; }

  .main-footer.is-aboard,
  .main-footer.is-aboard .frame-water,
  .main-footer.is-aboard .footer-frame::before,
  .main-footer.is-aboard .footer-plate,
  .main-footer.is-aboard .frame-bite-top,
  .main-footer.is-aboard .frame-bite-bottom,
  .main-footer.is-aboard .frame-outline,
  .main-footer.is-aboard .footer-portrait,
  .main-footer.is-aboard .footer-enquiry,
  .main-footer.is-aboard .footer-baseline,
  .main-footer.is-aboard .footer-baseline > * {
    animation: none;
  }

  .frame-sweep { display: none; }

  .decl-caret { display: none; }
  .decl-aside { display: none; }
}

/* --------------------------------------------------------------- the crew

   Two short columns either side of the officer, the way a chart's title block
   carries the surveying authority down one margin and the agent down the
   other. They flank rather than stack because the middle of this plate is
   taken: the man the plate is about is standing in it, and reading round him
   is what makes the row a frame for him instead of a list under him. */
.footer-crew {
  display: grid;
  /* Placed, not flowed: two children in a three-column grid land in columns
     one and two, which puts the second column behind the officer. */
  grid-template-columns: 1fr var(--crew-gap) 1fr;
  align-items: start;
  direction: ltr;
  margin-block-start: calc(clamp(1.5rem, 3vw, 2.5rem) + var(--decl-rise));
  padding-inline: var(--plate-pad);
  /* The same hole the pockets leave, so the columns and the bottom edge get
     out of his way by exactly the same amount. */
  --crew-gap: var(--officer-gap);
}

/* The other half of the lift: the height the footer gained at the top goes
   back in here, below the sentence, where nothing is being measured against
   it — so the crew, the instruments, the officer and the stamped line stay on
   the page exactly where they were and only the sentence and the signature
   travel. */
@media (min-width: 721px) {
  .footer-crew {
    margin-block-start: calc(clamp(1.5rem, 3vw, 2.5rem) + var(--lift) + var(--decl-rise));
  }
}

.footer-crew-col:first-child { grid-column: 1; }
.footer-crew-col:last-child { grid-column: 3; }

.footer-crew-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  direction: ltr;
  text-align: center;
}

/* No trailing rule in here: the rule is what ties a label to a row running off
   to the edge, and these two label short centred stacks. */
.footer-crew-col .footer-col-label {
  justify-content: center;
  margin-block-end: 0.85rem;
}

.footer-crew-col .footer-col-label::after { display: none; }

.footer-crew-col a,
.footer-crew-col span.crew-link {
  display: block;
  color: var(--foam);
  font-family: var(--foot-sans);
  font-size: clamp(1.3rem, 2.3vw, 1.875rem);
  font-weight: 800;
  font-stretch: 86%;
  letter-spacing: 0;
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
  transition: color var(--dur-quick, 160ms) var(--ease-out);
}

/* ------------------------------------------------------------------ the roll

   A name here is a destination, and pointing at one shows it leaving for it:
   the word rolls up out of its own line and the same word arrives from under
   it, in a box exactly one line tall that nothing is allowed to leave. Two
   copies, one moving out and one moving in, so the line is never empty and
   never grows — the column does not shift by a pixel while it plays.

   Letter by letter, opening from the reading edge: the split is done in
   js/catalog.js and the delays are set there, which is why the whole word
   still rolls as one piece if that never runs. The ease is the long tail of a
   flywheel — nearly all of the distance is covered in the first third of the
   time and the last of it is given away slowly, which is what makes a
   mechanical movement read as smooth rather than as a slide.

   `overflow: clip` and not `hidden`: `hidden` makes the box a scroll container,
   and a focus ring landing on a letter inside it scrolls the word sideways
   inside its own line. */
.crew-roll {
  position: relative;
  display: block;
  overflow: clip;
  /* The word is split into a span a letter for the roll, and a line may
     break between any two of them — WHATSAPP came apart on a phone. */
  white-space: nowrap;
}

.crew-face {
  display: block;
  white-space: nowrap;
  transform: translateY(0);
  transition: transform var(--roll-dur, 620ms) var(--roll-ease, cubic-bezier(0.16, 1, 0.3, 1));
}

/* The arriving copy is parked one line below and rides up on the same
   distance, so both faces move together and the exchange is exact. */
.crew-face + .crew-face {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
  width: 100%;
}

/* Split, the same thing is built one letter at a time: each letter gets its
   own box, its own pair of copies and its own delay, and the two faces above
   are replaced outright rather than nested inside — a face that rolled while
   its letters were also rolling would travel two lines and land on nothing. */
.crew-glyph {
  position: relative;
  display: inline-block;
  overflow: clip;
  vertical-align: top;
}

.crew-glyph > span {
  display: block;
  transform: translateY(0);
  transition: transform var(--roll-dur, 620ms) var(--roll-ease, cubic-bezier(0.16, 1, 0.3, 1));
  transition-delay: var(--stagger, 0ms);
}

.crew-glyph > span + span {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
}

.footer-crew-col a:hover,
.footer-crew-col a:focus-visible { color: var(--brass); }

.footer-crew-col a:hover .crew-face,
.footer-crew-col a:focus-visible .crew-face,
.footer-crew-col a:hover .crew-glyph > span,
.footer-crew-col a:focus-visible .crew-glyph > span {
  transform: translateY(-100%);
}

/* Nothing to roll into: a reader who has asked for stillness gets the brass
   and nothing else, which was always the part carrying the meaning. */
@media (prefers-reduced-motion: reduce) {
  .crew-face,
  .crew-glyph > span {
    transition: none;
  }

  .footer-crew-col a:hover .crew-face,
  .footer-crew-col a:focus-visible .crew-face,
  .footer-crew-col a:hover .crew-glyph > span,
  .footer-crew-col a:focus-visible .crew-glyph > span {
    transform: none;
  }
}

/* A contact with nowhere to point yet is set as text, not as a dead link:
   the row still reads, and nothing promises a destination it does not have. */
.footer-crew-col span.crew-link { color: rgba(180, 206, 226, 0.82); }

/* On a phone the officer moves out from between them and the two columns
   simply sit side by side above him — and he comes down in size, because the
   instrument row is behind him and on a narrow plate he was standing on all
   of it instead of part of it. */
@media (max-width: 720px) {
  .footer-crew {
    grid-template-columns: 1fr 1fr;
    gap: clamp(1rem, 5vw, 2rem);
  }

  .footer-crew-col a,
  .footer-crew-col span.crew-link {
    font-size: clamp(1.1rem, 5.6vw, 1.5rem);
  }

  .footer-portrait {
    --portrait-h: clamp(190px, 54vw, 300px);
  }

  /* The photograph ends where it was taken, and on a phone he is big enough
     that the cut is the first thing you see. The last stretch is faded out so
     he goes into the plate instead of stopping on top of it.

     It starts late on purpose. The instrument row passes behind him at about
     four fifths of his height, and a fade that opens before that turns him
     into a window: the marks slide across his chest and it is suddenly the
     row in front and the man behind. So he holds solid until the row is past
     him, and only lets go over the pocket, where the only thing behind him is
     plate. */
}

/* --------------------------------------------------------------- the officer

   Standing in the recess and through its bottom edge, onto the plate. He is
   the one thing on the page allowed to break the frame, and breaking it is
   what tells you the frame is a real edge and not a drawn one: nothing can
   overlap a border that is only a line.

   Absolutely placed rather than in the flow, so the instrument row runs the
   full width behind him instead of being pushed aside by a picture. */
.footer-portrait {
  position: absolute;
  z-index: 4;
  /* Cut at the panel's bottom edge: he stands in the panel, not over the
     band under it. */
  clip-path: inset(0 0 var(--plate-overhang) 0);
  /* Physical again, and sized by height: the space he has to stand in is
     vertical, and letting the width drive it made him grow past the plate. */
  bottom: calc(-1 * var(--plate-overhang));
  left: 50%;
  height: var(--portrait-h);
  transform: translateX(-50%);
  pointer-events: none;
}

.footer-portrait img {
  display: block;
  width: auto;
  height: 100%;
  /* Lifted off the water rather than pasted on it. */
  filter: drop-shadow(0 18px 34px rgba(2, 10, 20, 0.55));
}

/* ------------------------------------------------------------------ labels */

/* A label and the rule that runs out from it to the edge of the printed area,
   the way a chart captions a band along its own line. Left to right, with the
   row it labels. */
.footer-col-label {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  direction: ltr;
  margin-block-end: 0.9rem;
  /* Small, plain and grey, as the reference's are: an eyebrow says what the
     stack under it is and then gets out of its way. Tracked-out brass mono
     was a second voice competing with the links it labels. */
  color: rgba(244, 249, 252, 0.58);
  font-family: var(--foot-sans);
  font-size: 0.66rem;
  font-weight: 700;
  font-stretch: 100%;
  letter-spacing: 0.01em;
  line-height: 1;
  text-transform: uppercase;
}

.footer-col-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(233, 180, 76, 0.28);
}

/* ---------------------------------------------------------------- the tools

   The instruments the survey was made with, running along the foot of the
   recess and passing behind the officer. Marks, not controls. */
.footer-tools {
  position: relative;
  z-index: 1;
  margin-block-start: clamp(2rem, 4vw, 3rem);
  padding-block: clamp(1.25rem, 3vw, 2rem) calc(var(--bite-h) + clamp(0.75rem, 2vw, 1.5rem));
  padding-inline: var(--plate-pad);
}

/* The reference runs its marks along the foot of the panel with no caption
   and no rule: a row of wordmarks says what it is. The caption stays for
   anyone who is read the page rather than shown it. */
.footer-tools-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.marquee {
  overflow: hidden;
  /* Left to right, inside a right-to-left page, and this is what makes the
     loop a loop.

     In the page's own direction the track is laid against the right edge and
     overflows to the left; translating it further left pulls its right edge
     inward and empties the right of the panel, so the row visibly ran out and
     started over. Anchored to the left instead, the second copy is already
     sitting in the space the first one vacates and the seam never exists. */
  direction: ltr;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  --tool-gap: clamp(2.4rem, 5.5vw, 5rem);
  /* What the white inside a mark (the R in RStudio's disc) is filled with:
     the water it is printed on, so it reads as a hole in the brass. */
  --tool-knock: var(--sea-abyss);
  /* The height a wordmark is set at; each mark is sized against it by eye,
     because a logo's box is not its visual weight. */
  --tool-h: clamp(26px, 2.9vw, 38px);
}

/* The gap lives inside a set and trails it, never between the two sets. With
   the space between them owned by the track, one set's width and half the
   track's width are different numbers — and the keyframe below travels the
   second one, so the loop landed a gap short of where it started and showed a
   seam once every pass. */
.marquee-track {
  display: flex;
  align-items: center;
  width: max-content;
  padding-block: 0.5rem;
}

.marquee-set {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--tool-gap);
  padding-inline-end: var(--tool-gap);
}

/* The drift itself is js/catalog.js's (initMarquee): one copy's width in
   pixels, measured again whenever anything changes size, so it is never a
   percentage of a track that has since been rebuilt. Without the script the
   row simply stands still — full, never half-empty. */
.marquee-track {
  will-change: transform;
}

/* Nothing moves; the row becomes something you push instead. */
@media (prefers-reduced-motion: reduce) {
  .marquee {
    overflow-x: auto;
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* Marks, not controls. Nothing here is pressable, so nothing here lights up
   under a cursor or takes a tab stop — a row that reacts to being pointed at
   is a row that has promised something. */
.tool {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  color: var(--brass);
  pointer-events: none;
  user-select: none;
}

.tool svg {
  display: block;
  width: auto;
  height: var(--tool-h);
  fill: currentColor;
}

/* Optical sizes. Python's wordmark is light and wide, so it is set a touch
   taller; the NumPy and pandas marks carry a heavy symbol and are pulled in;
   R is a symbol alone and needs the height a wordmark gets from its width. */
.tool-python svg { height: calc(var(--tool-h) * 1.08); }
.tool-numpy svg  { height: calc(var(--tool-h) * 1.28); }
.tool-pandas svg { height: calc(var(--tool-h) * 1.18); }
.tool-r svg      { height: calc(var(--tool-h) * 1.18); }
.tool-gemini svg { height: calc(var(--tool-h) * 1.1); }
.tool-kaggle svg { height: calc(var(--tool-h) * 1.02); }
.tool-jupyter svg { height: calc(var(--tool-h) * 1.5); }
.tool-colab svg  { height: calc(var(--tool-h) * 0.92); }
.tool-deepseek svg { height: calc(var(--tool-h) * 0.9); }
.tool-perplexity svg { height: calc(var(--tool-h) * 0.95); }
.tool-huggingface svg { height: calc(var(--tool-h) * 1.18); }
.tool-vscode svg { height: calc(var(--tool-h) * 1.08); }
.tool-copilot svg { height: calc(var(--tool-h) * 1.05); }
.tool-w3schools svg { height: calc(var(--tool-h) * 1.4); }
.tool-openai svg, .tool-claude svg, .tool-github svg,
.tool-stackoverflow svg, .tool-rstudio svg { height: calc(var(--tool-h) * 0.96); }

/* --------------------------------------------------------------- the button

   One pill at the foot of the panel, centred, in the frame's own colour, the
   height of a pocket above the bottom edge. */
.footer-enquiry {
  position: absolute;
  z-index: 5;
  bottom: var(--bite-h);
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  padding: 0.95em 1.45em;
  border-radius: 10px;
  background: var(--ground);
  color: var(--ground-ink);
  font-family: var(--foot-sans);
  font-size: clamp(0.82rem, 1vw, 1.05rem);
  font-weight: 800;
  font-stretch: 86%;
  letter-spacing: 0;
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
  white-space: nowrap;
  direction: ltr;
  transform: translateX(-50%);
  transition: background-color var(--transition-fast, 160ms), color var(--transition-fast, 160ms);
}

.footer-enquiry svg {
  width: 1.05em;
  height: 1.05em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.footer-enquiry:hover,
.footer-enquiry:focus-visible {
  background: var(--brass);
  color: #04121f;
}

.main-footer.is-boarding .footer-enquiry { opacity: 0; }

@keyframes enquiry-stamp {
  from { opacity: 0; transform: translateX(-50%) scale(1.06); }
  to   { opacity: 1; transform: translateX(-50%); }
}

.main-footer.is-aboard .footer-enquiry {
  animation: enquiry-stamp 420ms var(--ease-stamp) calc(var(--survey-end) + 150ms) both;
}

@media (max-width: 720px) {
  .footer-enquiry {
    bottom: calc(var(--bite-h) + 0.35rem);
    padding: 0.8em 1.1em;
  }

  /* Room under the instrument row for the button, which sits over it on a
     phone otherwise. */
  .footer-tools {
    padding-block-end: calc(var(--bite-h) + 3.4rem);
  }
}

/* ------------------------------------------------------------- the baseline

   Stamped on the plate itself, outside the recess — the line engraved under a
   mounted chart rather than printed on it. Ink on brass, so it is read as
   metalwork and not as more of the page. */
/* Stamped in the two pockets, not printed under the plate: the row is pulled
   back up by exactly the height of the bites, so the copyright and the credit
   line sit inside the cuts they were made for. */
.footer-baseline {
  position: relative;
  z-index: 5;
  display: flex;
  /* Copyright at the left, the credit at the right — physical, like the
     corners the pockets are cut in. */
  direction: ltr;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
  /* The pocket is the cut plus the band under it, and the line sits in the
     middle of the whole pocket. */
  height: calc(var(--bite-h) + var(--plate-overhang));
  margin-block-start: calc(-1 * var(--bite-h));
  margin-block-end: calc(-1 * var(--plate-overhang));
  padding-inline: clamp(0.9rem, 2.2vw, 1.9rem);
  color: var(--ground-ink);
  font-size: 0.82rem;
  font-weight: 800;
}

/* The Latin half of the line is set in the footer's grotesk; the Arabic
   credit keeps the body face. */
.footer-baseline > span {
  font-family: var(--foot-sans);
  font-stretch: 93%;
}

.footer-credits {
  margin: 0;
  padding-inline-start: 0.7rem;
  border-inline-start: 3px solid color-mix(in srgb, currentColor 50%, transparent);
  color: var(--ground-ink);
  font-weight: 800;
}

/* Set smaller rather than stacked or dropped: two short lines, one at each
   end of the edge they are stamped in, is the shape — and it is the shape at
   every width. Stacking them turned the foot of the page into a paragraph. */
@media (max-width: 720px) {
  .footer-baseline {
    padding-inline: clamp(0.55rem, 2.4vw, 0.9rem);
    /* Set off the window, not off a breakpoint: the two lines have to finish
       inside their pockets on a 320 as well as on a 430, and the pocket is
       the same fraction of the width at both. */
    font-size: clamp(0.58rem, 2.6vw, 0.72rem);
  }

  /* inherit, because a bare `p` rule elsewhere sets a size and a set size
     beats an inherited one — the tagline was the one thing on this row that
     did not get smaller with the row. */
  .footer-credits {
    padding-inline-start: 0.5rem;
    border-inline-start-width: 2px;
    font-size: inherit;
  }
}


/* ================================================================ the phone

   On a phone the two pockets go, and the bottom edge takes the shape the top
   edge has, turned over: one tab hanging from the middle of the panel, the
   officer standing down into it, the button across its mouth, and one line of
   type — the copyright at one end, the credit at the other — on the band of
   page under the whole thing. Written last so it wins over every phone rule
   above it. */
.frame-bite-bottom { display: none; }

@media (max-width: 720px) {
  .main-footer {
    /* The tab's depth, and with it the proportions of its curves. */
    --bite-h: clamp(16px, 4.5vw, 22px);
    --foot-fillet: calc(var(--bite-h) * 0.5);
    --foot-shoulder: calc(var(--bite-h) * 1.5);
    /* Wide enough for him with a margin either side, and never narrower than
       the reference's share of the width. */
    --foot-w: max(62vw, calc(0.87 * var(--portrait-h) + 2rem));
  }

  .frame-bite-bl,
  .frame-bite-br { display: none; }

  .frame-bite-bottom {
    display: block;
    box-sizing: border-box;
    top: calc(100% - 1px);
    left: 50%;
    width: var(--foot-w);
    height: calc(var(--bite-h) + 1px);
    border-radius: 0 0 var(--foot-fillet) var(--foot-fillet);
    background: var(--sea-abyss);
    transform: translateX(-50%);
  }

  .frame-bite-bottom::before,
  .frame-bite-bottom::after {
    top: -1px;
    width: calc(var(--foot-shoulder) + 1px);
    height: calc(var(--foot-fillet) + 1px);
    background: var(--sea-abyss);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
  }

  .frame-bite-bottom::before {
    right: calc(100% - 1px);
    -webkit-mask-image: radial-gradient(var(--foot-shoulder) var(--foot-fillet) at 0 100%,
      transparent 98%, #000 100%);
    mask-image: radial-gradient(var(--foot-shoulder) var(--foot-fillet) at 0 100%,
      transparent 98%, #000 100%);
  }

  .frame-bite-bottom::after {
    left: calc(100% - 1px);
    -webkit-mask-image: radial-gradient(var(--foot-shoulder) var(--foot-fillet) at 100% 100%,
      transparent 98%, #000 100%);
    mask-image: radial-gradient(var(--foot-shoulder) var(--foot-fillet) at 100% 100%,
      transparent 98%, #000 100%);
  }

  /* Down into the tab, and cut where the tab ends. */
  .footer-portrait {
    bottom: calc(-1 * var(--bite-h));
    clip-path: none;
  }

  /* Across the mouth of the tab: a quarter of the tab's depth below the
     panel's edge, as in the reference. */
  .footer-enquiry {
    bottom: calc(var(--bite-h) * -0.25);
    padding: 0.85em 1.3em;
    font-size: 0.8rem;
  }

  .footer-tools {
    padding-block-end: 3.2rem;
  }

  /* One line on the band under the panel, clear of the tab. */
  .footer-baseline {
    height: 2.75rem;
    margin-block: var(--bite-h) 0;
    padding-inline: clamp(0.9rem, 4vw, 1.4rem);
    font-size: clamp(0.62rem, 2.8vw, 0.76rem);
  }
}
