/* ================================================
   Identity: "LKD: Architect v4"
   paper #F2EDE2 · ink #141518 · blueprint #2651A8 · Geist
   Light only. Blue is an accent, never a surface.
   ================================================ */
:root {
  --paper-50: #faf8f3;
  --paper-100: #f6f2e9;
  --paper-200: #f2ede2;
  --paper-300: #e9e3d5;
  --paper-400: #cfcac2;
  --ink-500: #777672;
  --ink-800: #2d2e34;
  --ink-950: #141518;
  --blueprint: #2651a8;
  --faint: rgba(20, 21, 24, 0.38);
  --dot: rgba(20, 21, 24, 0.11);
  --border-soft: rgba(20, 21, 24, 0.09);
  --red: #b3261e;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  color-scheme: light;
}

body {
  /* the desk: flat, slightly deeper than the sheet */
  background: var(--paper-200);
  color: var(--ink-800);
  font-family: "Geist", system-ui, -apple-system, sans-serif;
  min-height: 100svh;
  display: flex;
  padding: 14px;
}
@media (min-width: 768px) {
  body {
    padding: 28px;
  }
}

::selection {
  background: var(--blueprint);
  color: var(--paper-50);
}

a {
  color: inherit;
}
a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--blueprint);
  outline-offset: 2px;
}

/* the sheet: dot-grid drafting paper on the desk */
.sheet {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: var(--paper-50);
  /* Isometric dot paper: two offset grids interleave into the triangular
     field. At rest it is drawing-paper texture, the same weight and
     density as the main site's grid (a dot per ~580 square px, 1px wide),
     just laid out isometrically. Entering the test zooms it in to the
     drawing pitch (step 26 -> step 56), the lattice the tool reads. */
  background-image:
    radial-gradient(circle, var(--dot) 1px, transparent 1.5px),
    radial-gradient(circle, var(--dot) 1px, transparent 1.5px);
  background-size:
    45.03px 26px,
    45.03px 26px;
  background-position:
    0 0,
    22.517px 13px;
  transition:
    background-size 780ms cubic-bezier(0.3, 0.8, 0.25, 1),
    background-position 780ms cubic-bezier(0.3, 0.8, 0.25, 1);
  border: 1px solid var(--paper-400);
}

/* the paper zooms in to the drawing pitch, dots gaining just enough
   weight to aim at */
.sheet.zoomed {
  background-image:
    radial-gradient(circle, var(--dot) 1.6px, transparent 2.1px),
    radial-gradient(circle, var(--dot) 1.6px, transparent 2.1px);
  background-size:
    96.99px 56px,
    96.99px 56px;
  background-position:
    0 0,
    48.497px 28px;
  box-shadow:
    0 1px 2px rgba(20, 21, 24, 0.04),
    0 8px 32px rgba(20, 21, 24, 0.05);
}

/* ISO margin apparatus */
.frame-ref {
  position: absolute;
  font-weight: 500;
  font-size: 0.55rem;
  letter-spacing: 0.1em;
  color: var(--faint);
}
.frame-tick {
  position: absolute;
  background: var(--faint);
}
.center-mark {
  position: absolute;
  background: var(--ink-800);
  opacity: 0.45;
}
@media (max-width: 767px) {
  .frame-ref,
  .frame-tick,
  .center-mark,
  .md-only {
    display: none;
  }
  /* .btn sets its own display further down the sheet, and one class does not
     beat one class when the other comes later. Named twice, it wins. */
  .btn.md-only {
    display: none;
  }
}

/* annotations: Geist Medium, +14% tracking, uppercase */
.note {
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-500);
}
.note b {
  color: var(--ink-800);
  font-weight: 500;
}

header {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--paper-50);
  padding: 10px 14px;
  border-bottom: 1px solid var(--paper-400);
  white-space: nowrap;
}
@media (min-width: 768px) {
  header {
    padding: 10px 24px;
  }
}

main {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 24px 14px;
}
@media (min-width: 768px) {
  main {
    padding: 24px;
  }
}
/* while the test is on, the plate flows from the top so a tall
   panel can never be centred out of reach, with a band left clear
   at the top for the shrunken writing */
main.playing {
  justify-content: flex-start;
  padding-top: 64px;
}
@media (max-width: 767px) {
  main.playing {
    padding-top: 82px;
  }
  .field.flown {
    top: 8px;
    left: 14px;
    transform: scale(0.34);
  }
}

/* centered column, text left-aligned inside. It sits above the massing,
   which turns under its edge. */
.field {
  /* the width the flag below sets by hugging its own line: the intro is
     held to it so the two share a right-hand edge */
  --measure: 33rem;
  position: relative;
  z-index: 1;
  /* Shrunk to the writing, so the block is centred on the sheet on its own
     width. The figure is placed off that width rather than off the sheet's
     edge, so the two cannot meet. */
  width: fit-content;
  max-width: 100%;
  /* The corner it flies to is the corner it is scaled about, in both
     directions. The flight home is measured while the block is back in
     flow, so if the origin belonged to the flown state alone the measured
     move would be composed about the middle instead and the writing would
     jump before it set off. */
  transform-origin: top left;
  overflow: hidden;
  /* the same travel, at the same speed, in both directions */
  transition:
    opacity 380ms ease,
    transform 780ms cubic-bezier(0.3, 0.8, 0.25, 1);
}
/* they fade out on the way up and back in on the way down, rather than
   snapping back the moment the class goes */
.field .intro,
.field .hold,
.field .cta-row {
  transition: opacity 340ms ease 140ms;
}
/* The writing flies into the top-left corner and shrinks to an
   annotation, leaving the middle of the sheet free for the board.
   It is taken out of flow, so it reserves no space on the way. */
.field.flown {
  position: absolute;
  /* left:0 would sit against the border, so the sheet's own margin is
     added back and the annotation lines up with the drawing below it */
  top: 12px;
  left: 24px;
  overflow: visible;
  transform: scale(0.42);
  pointer-events: none;
  transition:
    transform 780ms cubic-bezier(0.3, 0.8, 0.25, 1),
    opacity 400ms ease;
}
/* only the name survives the trip; the rest dissolves as it goes */
.field.flown .intro,
.field.flown .hold,
.field.flown .cta-row {
  opacity: 0;
  transition: opacity 300ms ease;
}
.field.flown .keyline {
  opacity: 0.5;
}

h1 {
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.05;
  text-wrap: balance;
  color: var(--ink-800);
  font-size: 2.1rem;
}
/* sized so the line ends flush with the name line's right edge */
.h1-sub {
  display: block;
  margin-top: 0.3em;
  font-size: 0.666em;
  font-weight: 500;
  letter-spacing: -0.02em;
}
@media (min-width: 768px) {
  h1 {
    font-size: clamp(2.4rem, 3.6vw, 3.4rem);
  }
}

/* square period: the precise touch */
.sq {
  display: inline-block;
  width: 0.135em;
  height: 0.135em;
  background: var(--blueprint);
  margin-left: 0.07em;
}

.keyline {
  height: 5px;
  width: 74px;
  background: var(--blueprint);
  margin: 20px 0;
}

.intro {
  /* The same measure as the revision flag below it, so both stop on one
     right-hand edge and the sentence sets on two lines. Not a percentage:
     the column round it is sized to fit its content, and a percentage does
     not constrain that, so the sentence would set the column's width and
     drag the heading out with it. */
  max-width: var(--measure);
  font-size: 1rem;
  color: var(--ink-500);
  line-height: 1.55;
}

/* The figure turns under the writing, so the letters carry a little of the
   sheet with them: a soft halo in the paper's own colour, invisible against
   bare paper, holding the words clear where a line crosses them. */
h1,
.intro {
  text-shadow:
    0 0 2px var(--paper-50),
    0 0 4px var(--paper-50),
    0 0 7px var(--paper-50);
}
.intro strong {
  font-weight: 700;
  color: var(--ink-800);
}
@media (min-width: 768px) {
  .intro {
    font-size: 1.125rem;
  }
}

/* HOLD: the revision flag. Anything inside is not final content. */
.hold {
  position: relative;
  /* hugs its line rather than stretching to the column */
  width: fit-content;
  max-width: 100%;
  margin-top: 32px;
  padding: 18px 20px 16px;
  border: 1.5px dashed rgba(38, 81, 168, 0.55);
  background: rgba(38, 81, 168, 0.05);
}
.hold-tag {
  position: absolute;
  top: -9px;
  left: 12px;
  padding: 2px 8px;
  background: var(--blueprint);
  color: #fff;
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}
.hold p {
  font-size: 0.9375rem;
  color: var(--ink-500);
  line-height: 1.55;
}
.hold p strong {
  font-weight: 500;
  color: var(--ink-800);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  margin-top: 28px;
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--ink-800);
  color: var(--paper-200);
  border: 1px solid var(--ink-800);
  text-decoration: none;
  font-family: inherit;
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 12px 18px;
  cursor: pointer;
}
.btn:hover {
  background: var(--ink-950);
  border-color: var(--ink-950);
}
.btn-ghost {
  background: transparent;
  color: var(--ink-800);
  border: 1px solid var(--paper-400);
}
.btn-ghost:hover {
  background: transparent;
  border-color: var(--ink-800);
}
.text-link {
  font-family: inherit;
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-800);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: var(--paper-400);
}
.text-link:hover {
  text-decoration-color: var(--ink-800);
}
/* ================================================
   V2 · THE ENDLESS COLUMN
   Standing the full height of the sheet, turning, behind the writing.
   ================================================ */
.column {
  position: absolute;
  /* the whole dotted field: the column turns in the middle of the sheet and
     runs off both ends of it, so what you see is a length of something with
     no ends, not an object standing in a corner */
  inset: 0;
  z-index: 0;
  display: none;
  align-items: center;
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  /* the ends run past the sheet and are cut by it, so it reads as endless */
  overflow: hidden;
  /* it covers the field now, so only the drawing itself answers the cursor
     and everything else on the sheet stays reachable */
  pointer-events: none;
}
.col-fill,
.col-line {
  pointer-events: auto;
}
/* No box: a rectangle around something this tall reads as a mistake. The
   lines answer instead, on plain focus so the mark is certain to appear. */
.column:focus-visible {
  outline: none;
}
.column:focus .col-line {
  stroke-width: 2.4;
}
body.v2 .column {
  display: flex;
}
.column svg {
  display: block;
  height: 100%;
  width: 100%;
  overflow: visible;
}
/* the paper the column stands on: the dots stop at its outline */
.col-fill {
  fill: var(--paper-50);
  stroke: none;
}
.col-line {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
}
main.playing .column {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 380ms ease,
    visibility 0s 380ms;
}

/* the switch, while the shape is being chosen */
.version-switch {
  position: fixed;
  left: 14px;
  bottom: 14px;
  z-index: 8;
  display: flex;
  gap: 4px;
}
.version-switch button {
  font-family: inherit;
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  padding: 6px 10px;
  color: var(--ink-500);
  background: var(--paper-50);
  border: 1px solid var(--paper-400);
  cursor: pointer;
}
.version-switch button.is-on {
  background: var(--blueprint);
  border-color: var(--blueprint);
  color: #fff;
}

/* The site name in the footer doubles as the way home once the test is
   on: it reads as plain text until there is somewhere to go back to. */
.home-link {
  /* One of the sheet's annotations, so it is set like the rest of them: the
     note class supplies the size, the tracking, the case and the grey, and
     only the typeface has to be taken back off the button. The padding gives
     something to hit at that size and is taken out again by the margin, so
     the strip does not move. */
  font-family: inherit;
  background: none;
  border: 0;
  padding: 6px 8px;
  margin: -6px -8px;
  cursor: default;
}
/* it only becomes a way out once the test is running, and it only
   shows its underline under the cursor */
main.playing ~ footer .home-link,
body:has(main.playing) .home-link {
  cursor: pointer;
  text-decoration: none;
}
body:has(main.playing) .home-link:hover,
body:has(main.playing) .home-link:focus-visible {
  color: var(--ink-800);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: var(--ink-800);
}

/* ================================================
   V3 · THE ARCADE
   A small building standing in the right margin, turned by hand.
   ================================================ */
.arcade {
  position: absolute;
  /* Clear of the writing, which is centred on its own width. The heading
     sets that width, and the heading is sized in vw over the range that
     matters, so half the block is a near-constant share of the sheet: past
     that share the figure has the margin to itself. Above the size where the
     heading stops growing the gap only opens further. */
  left: 73%;
  right: 1.5%;
  top: 50%;
  transform: translateY(-50%);
  z-index: 0;
  display: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
body.v3 .arcade {
  display: block;
}
/* on a narrower sheet it steps back rather than climbing over the writing */
@media (max-width: 1200px) {
  .arcade {
    left: 72%;
    right: 1%;
  }
}
@media (max-width: 940px) {
  body.v3 .arcade {
    display: none;
  }
}
.arcade svg {
  display: block;
  width: 100%;
  height: auto;
}
/* the paper the building stands on: the dots stop at its outline */
.arc-fill {
  fill: var(--paper-50);
  stroke: none;
}
.arc-line {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
}
.arcade:focus-visible {
  outline: none;
}
/* keyboard focus is shown on the drawing itself, not as a box round it */
.arcade:focus .arc-line {
  stroke-width: 2.4;
}
main.playing .arcade {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 380ms ease,
    visibility 0s 380ms;
}

/* ================================================
   V4 . THE PROBLEM
   A plate in the right margin: solids standing on one plane, the shadow
   each of them throws, and the construction that finds it.

   Four weights, and the order they are painted in is what takes the
   hidden work out. The wash and the construction go down on the plane
   first; the paper mask goes over them, so anything lying behind a mass
   is covered by the mass; the ruled lines go on last.
   ================================================ */
.problem {
  position: absolute;
  /* The plate carries its field of shadow out to one side, so the pair of
     it and the writing sits better by eye a little left of where the sheet
     would centre them. Both are moved by the same 80px: the plate off its
     own edge, and the writing by twice that as a margin, since it is
     centred on its own width and a margin only moves it by half. */
  right: calc(2% + 80px - var(--reach));
  top: 50%;
  /* behind the writing, so it turns in and out from under the last line */
  transform: translateY(-50%);
  z-index: 0;
  display: none;
  /* a wide plate: the shadows lie out to one side of the building, so it
     needs the room the arcade did not */
  --plate: 520px;
  /* The hand turns it from a margin of paper around it, not from the
     drawing alone. The padding is the margin; the box grows by it on both
     sides and the offset gives the same amount back, so the drawing itself
     does not move or change size. */
  --reach: 120px;
  box-sizing: border-box;
  width: calc(var(--plate) + var(--reach) * 2);
  padding: var(--reach);
  background: none;
  border: 0;
  cursor: pointer;
}
body.v4 .problem {
  display: block;
}
/* on a narrower sheet it steps back rather than climbing over the writing */
@media (max-width: 1200px) {
  .problem {
    --plate: 380px;
    --reach: 70px;
    right: calc(1% + 80px - var(--reach));
  }
}
/* only while the plate is on the sheet at all: below that it is gone, and
   the writing centres the way it does everywhere else */
@media (min-width: 941px) {
  body.v4 .field {
    margin-right: 160px;
  }
}
@media (max-width: 940px) {
  body.v4 .problem {
    display: none;
  }
}
.problem svg {
  display: block;
  width: 100%;
  height: auto;
}
/* the shadows: one wash, whatever it is made of, because the whole field
   is painted as a single union and never doubles up on itself */
.pb-shade {
  fill: var(--ink-800);
  stroke: none;
  opacity: 0.15;
}
/* the shadows the plate rules an outline for, because each of them is an
   answer the construction beside it has just found: the cube's sheared
   square, the sphere's ellipse, and the two solids standing free */
.pb-cast {
  fill: none;
  stroke: var(--ink-800);
  stroke-width: 1;
  opacity: 0.38;
  vector-effect: non-scaling-stroke;
}
/* the construction, in a lighter line, because a construction is not a
   thing: the plumb lines, the lines on the plane, the axes and the ticks */
.pb-thin {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  opacity: 0.55;
  vector-effect: non-scaling-stroke;
}
/* the rays of light, broken, the way a drawing marks something that is not
   built */
.pb-ray {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  opacity: 0.55;
  stroke-dasharray: 3 2.5;
  vector-effect: non-scaling-stroke;
}
/* the paper the solids stand on: the dots and the construction both stop at
   their outline */
.pb-fill {
  fill: var(--paper-50);
  stroke: none;
}
/* the shade on the solids themselves, hatched: graphite, like the wash on
   the plane, so light and dark are one family and the line work stays the
   other one */
.pb-hatch {
  fill: none;
  stroke: var(--ink-800);
  stroke-width: 1;
  opacity: 0.24;
  vector-effect: non-scaling-stroke;
}
.pb-line {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
}
.problem:focus-visible {
  outline: none;
}
/* keyboard focus is shown on the drawing itself, not as a box round it */
.problem:focus .pb-line {
  stroke-width: 2.4;
}
main.playing .problem {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 380ms ease,
    visibility 0s 380ms;
}

/* the mark on an action: a tick for checking, an arrow for going on */
.btn-icon {
  display: inline-block;
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

.arrow {
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
}
.arrow path {
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
}

footer {
  position: relative;
  z-index: 2;
  background: var(--paper-50);
  border-top: 1px solid var(--paper-400);
}
footer .row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 14px;
}
@media (min-width: 768px) {
  footer .row {
    padding: 6px 24px;
  }
}

/* the invitation, carried by the cursor while a hand is on the massing */
.play-bubble {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
  transform: translate(-200px, -200px);
  background: var(--ink-800);
  color: var(--paper-200);
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 8px 12px;
  white-space: nowrap;
  transition:
    opacity 220ms ease,
    transform 130ms linear;
  will-change: transform;
}
.play-bubble.is-on {
  opacity: 1;
}
/* One follower at a time: the set square steps back for the bubble. Every
   figure carries the same invitation, so every figure has to step it back. */
body:has(.arcade:hover) .equerre,
body:has(.column:hover) .equerre,
body:has(.problem:hover) .equerre {
  opacity: 0;
}

/* the equerre: a set square trailing the cursor, hairline only.
   Fine pointers only, enabled from JS. */
.equerre {
  position: fixed;
  top: 0;
  left: 0;
  width: 100px;
  height: 64px;
  pointer-events: none;
  z-index: 5;
  opacity: 0;
  /* It is on the cursor, not after it: the position is never eased. Only
     the fade in and out is timed. */
  transition: opacity 400ms ease;
  will-change: transform;
}
.equerre.is-on {
  opacity: 1;
}
.equerre .edge {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1.3;
  opacity: 0.42;
}
.equerre .inner {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1;
  opacity: 0.22;
}
.equerre .grad {
  stroke: var(--blueprint);
  stroke-width: 1;
  opacity: 0.3;
}
@media (prefers-reduced-motion: reduce) {
  .equerre {
    transition: opacity 400ms ease;
  }
}

/* ================================================
   THE DRAWING TEST
   ================================================ */
/* the board dissolves into the space the writing leaves behind, and it
   takes the whole sheet: the paper is the drawing surface */
.studio {
  position: relative; /* the marked plate is ruled against this */
  width: 100%;
  max-width: none;
  flex: 1;
  display: flex;
  flex-direction: column;
  opacity: 0;
  /* Opacity only: a transform here would shift the board's measured box
     and pull the drawing lattice off the sheet's printed dots. Leaving is
     quick and immediate, so the sheet is clear for the writing coming
     home; arriving waits for the writing to be out of the way. */
  transition: opacity 260ms ease;
}
.studio.is-in {
  opacity: 1;
  transition: opacity 620ms ease 260ms;
}
/* On the way out it stops taking space at once and is left only with its
   fade. It is a flex:1 child, so while it still counted the writing was
   flying home to a destination that moved under it the moment the studio
   was finally taken out of the flow. */
.studio.is-out {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.studio[hidden] {
  display: none;
}

/* the brief: what to draw, and how, in one block beside the reference */
.plate-brief {
  max-width: 34em;
}
.plate-how {
  margin-top: 12px;
  line-height: 1.7;
}

.plate-head[hidden],
.board[hidden] {
  display: none;
}
.plate-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 10px;
}
.plate-title {
  font-weight: 600;
  letter-spacing: -0.02em;
  font-size: 1.25rem;
  color: var(--ink-800);
}
.plate-hint {
  font-size: 0.875rem;
  color: var(--ink-500);
  margin-top: 2px;
}

/* the reference: the figure to reproduce, as a small plate */
/* the reference sits a shade deeper than the sheet, so its own grid reads
   instead of fighting the paper's dots showing through */
.ref {
  position: relative;
  flex: none;
  /* a narrow sheet cannot spare the width, or the brief beside it is
     squeezed to one word a line */
  width: 150px;
  border: 1px solid var(--paper-400);
  background: var(--paper-100);
  padding: 6px;
}
.ref svg {
  display: block;
  width: 100%;
  height: auto;
}
/* the reference number goes in the corner of its own frame, the way it is
   written on a drawing, not on a line underneath it */
.ref-label {
  position: absolute;
  top: 6px;
  right: 8px;
  text-align: right;
}
@media (min-width: 768px) {
  .ref {
    width: 260px;
  }
}
/* A laptop is wide but short. At that height the reference was taking half
   the studio and the drawing was left with its bare minimum, so the plate
   gives the paper its room back. */
@media (min-width: 768px) and (max-height: 920px) {
  .ref {
    width: 190px;
  }
}

/* the tool palette: pen or rubber, sitting under the reference */
.ref-column {
  flex: none;
}
/* ranged right, so the tools end on the same line as the button that
   ends the plate, and the same 6px off the frame as between them */
.tool-palette {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  justify-content: flex-end;
}
.tool {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid var(--paper-400);
  color: var(--ink-500);
  cursor: pointer;
  padding: 0;
}
.tool svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.tool:hover {
  border-color: var(--ink-800);
  color: var(--ink-800);
}
.tool.is-on {
  background: var(--blueprint);
  border-color: var(--blueprint);
  color: #fff;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* You draw straight onto the sheet: no frame, no second surface. The
   paper's own dots show through, and only the lines you rule are added. */
.board {
  position: relative;
  flex: 1;
  min-height: 240px;
  touch-action: none;
}
.board svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}
.board-surface {
  fill: transparent;
  cursor: crosshair;
}
/* where the plate suggests you begin */
.start-mark {
  fill: var(--blueprint);
}

/* the opening line ruling itself in */
.seg.ruling {
  stroke-dasharray: var(--len);
  animation: rule-in 380ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
@keyframes rule-in {
  from {
    stroke-dashoffset: var(--len);
  }
  to {
    stroke-dashoffset: 0;
  }
}
.anchor-ring {
  fill: none;
  stroke: var(--blueprint);
  stroke-width: 1.2;
}
.seg {
  stroke: var(--ink-800);
  stroke-width: 2.4;
  stroke-linecap: round;
}
.seg.correct {
  stroke: var(--blueprint);
}
.seg.extra {
  stroke: var(--red);
  opacity: 0.5;
}
.seg.missing {
  stroke: var(--blueprint);
  opacity: 0.42;
  stroke-dasharray: 4 5;
}
.preview {
  stroke: var(--blueprint);
  stroke-width: 2.4;
  stroke-linecap: round;
  opacity: 0.4;
}

/* the dot paper inside the reference, so the size can be counted */
.ref-dot {
  fill: var(--faint);
  opacity: 0.5;
}

/* the rubber: fat invisible lines, inert until the rubber is picked up */
.seg-rub {
  stroke: transparent;
  stroke-width: 16;
  fill: none;
  pointer-events: none;
}
.board.rubbing .seg-rub {
  pointer-events: stroke;
  cursor: pointer;
}
.board.rubbing .board-surface {
  cursor: default;
}
.btn-ghost.is-on {
  background: var(--blueprint);
  border-color: var(--blueprint);
  color: #fff;
}

/* the way out, the tally, and the way on: one line under the drawing */
.tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: 12px;
}
/* A phone has no height to spare. Left to itself this row stacks into three,
   which was enough to push the button that ends the plate off the bottom of
   the screen, and the paper cannot be scrolled past because it is the paper
   you draw on. Back and the button keep one line and the instruction goes
   under them. */
@media (max-width: 767px) {
  .tools {
    gap: 8px;
    margin-top: 8px;
  }
  #mode-hint {
    order: 3;
    flex-basis: 100%;
    text-align: center;
    line-height: 1.3;
  }
}
.tools .spacer {
  flex: 1;
}
#drawn-count {
  font-variant-numeric: tabular-nums;
}
/* the hidden attribute must win over display: flex */
.tools[hidden] {
  display: none;
}

.verdict {
  margin-top: 12px;
  border-top: 1px solid var(--paper-400);
  padding: 14px 0 4px;
}

/* The marked plate: a panel in the bottom corner where the controls were,
   so Next plate lands exactly where Check the plate stood and the drawing
   it is talking about stays in view. */
@media (min-width: 768px) {
  .verdict-panel {
    position: absolute;
    right: 0;
    bottom: 0;
    z-index: 3;
    width: min(400px, 46%);
    margin-top: 0;
    border: 1px solid var(--paper-400);
    background: var(--paper-100);
    padding: 16px 18px 18px;
  }
  /* the button keeps the exact place the primary action held: pulled out
     through the panel's own padding and border to the corner */
  .verdict-panel .cta-row {
    justify-content: flex-end;
    margin-right: -19px;
    margin-bottom: -19px;
  }
}
.verdict[hidden] {
  display: none;
}
.verdict-score {
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  color: var(--ink-800);
}
/* the score carries the weight, the total behind it stays light */
.verdict-score span {
  color: var(--blueprint);
  font-weight: 400;
}
.verdict-line {
  font-size: 0.875rem;
  color: var(--ink-500);
  margin-top: 2px;
}
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 10px;
}
.legend i {
  display: inline-block;
  width: 16px;
  height: 0;
  border-top: 2px solid currentColor;
  margin-right: 6px;
  vertical-align: middle;
}
.legend .ok {
  color: var(--blueprint);
}
.legend .no {
  color: var(--red);
  opacity: 0.6;
}
.legend .miss {
  color: var(--blueprint);
  opacity: 0.5;
}

/* ================================================
   THE LAST PLATE
   The test is over and the sheet is cleared for the result, without
   ever leaving the page: score first, then what it means, then the
   two ways on.
   ================================================ */
.result {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.result[hidden] {
  display: none;
}
.result-inner {
  width: 100%;
  max-width: 440px;
  text-align: center;
}

/* the score, the way a game shows it: the number carries, the total
   behind it stays light */
.result-total {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  font-size: clamp(3.4rem, 9vw, 5.5rem);
  color: var(--ink-800);
  margin-top: 10px;
}
.result-total span {
  font-weight: 400;
  color: var(--blueprint);
}
.result-rank {
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.35;
  font-size: 1.0625rem;
  color: var(--ink-500);
  margin-top: 12px;
  text-wrap: balance;
}

.result-rows {
  margin: 26px 0 4px;
}
.result-rows div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid var(--border-soft);
  padding: 8px 0;
}
.result-row-score {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 1rem;
  color: var(--ink-800);
}

.result-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 28px;
}
.result-note {
  font-size: 0.8125rem;
  color: var(--ink-500);
  margin-top: 14px;
}
.result-note a {
  color: var(--blueprint);
  text-underline-offset: 3px;
}

/* the shrunken writing steps aside for the last plate */
main.finished .field {
  opacity: 0;
  visibility: hidden;
}
.notify {
  margin-top: 22px;
}
.notify-form {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
}
.notify-form input {
  flex: 1;
  min-width: 200px;
  font-family: inherit;
  font-size: 0.9375rem;
  color: var(--ink-800);
  background: var(--paper-50);
  border: 1px solid var(--paper-400);
  padding: 11px 12px;
}
.notify-form input::placeholder {
  color: var(--ink-500);
}
.notify-note {
  font-size: 0.8125rem;
  color: var(--ink-500);
  margin-top: 8px;
}

/* load choreography: one easing, one duration, 12px of travel */
@media (prefers-reduced-motion: no-preference) {
  .rise {
    opacity: 0;
    transform: translateY(12px);
    animation: rise 650ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
  }
  .d1 {
    animation-delay: 60ms;
  }
  .d2 {
    animation-delay: 150ms;
  }
  .d3 {
    animation-delay: 260ms;
  }
  .d4 {
    animation-delay: 380ms;
  }
  .d5 {
    animation-delay: 500ms;
  }
  @keyframes rise {
    to {
      opacity: 1;
      transform: none;
    }
  }
  .keyline {
    transform-origin: left;
    transform: scaleX(0);
    animation: bar 600ms 700ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
  }
  @keyframes bar {
    to {
      transform: scaleX(1);
    }
  }
}
