/* Loose Stitch: a poster cut of the darkroom. The rug is the picture, edge to edge; one lime means "look here". */

*,
*::before,
*::after { box-sizing: border-box; }

html,
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
}

body {
  background-color: var(--ak-bg);
  background-image: var(--ak-backdrop-layer, var(--ak-bg-art));
  background-size: cover;
  background-position: center;
  color: var(--ak-ink);
  font-family: var(--ak-font-body);
  -webkit-tap-highlight-color: transparent;
}

[hidden] { display: none !important; }

/* Measures come from LooseStitch.LAYOUT (game.js sets them); these are the same numbers. */
:root {
  --pad: 8px;
  --pad-b: 4px;
  --seam: 1px;
  --logo-h: 40px;
  --panel-min: 176px;
  --panel-min-w: 240px;
  --panel-min-small: 230px;
  --help-clear: 72px;
  --edge: 0px;
  --status: var(--ac-status-h, 60px);
  --in-l: max(var(--pad), env(safe-area-inset-left));
  --in-r: max(var(--pad), env(safe-area-inset-right));
  --in-b: max(var(--pad-b), env(safe-area-inset-bottom));
}

.play {
  position: fixed;
  inset: 0;
  display: grid;
  justify-content: center;
  /* Upright the rug runs edge to edge; only the safe areas hold it in. */
  padding: var(--status) max(var(--edge), env(safe-area-inset-right)) var(--in-b) max(var(--edge), env(safe-area-inset-left));
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  /* Upright: the logo, then the rug, then the words under it. */
  --side: min(
    100dvw - 2 * max(var(--edge), env(safe-area-inset-left), env(safe-area-inset-right)),
    100dvh - var(--status) - var(--logo-h) - var(--panel-room) - var(--in-b)
  );
  --panel-room: var(--panel-min);
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: var(--logo-h) var(--side) minmax(0, 1fr);
  grid-template-areas: "mast" "frame" "panel";
}

.play.is-small { --panel-room: var(--panel-min-small); }

@media (orientation: landscape) {
  .play {
    /* Sideways: the words under the status chips on the left; the rug on the right, beside the chips
       and short of the help button, so it can run the full height of the screen. */
    padding: var(--pad) calc(var(--help-clear) + env(safe-area-inset-right)) var(--in-b) var(--in-l);
    --side: min(
      100dvh - var(--pad) - var(--in-b),
      100dvw - var(--panel-min-w) - var(--in-l) - var(--pad) - var(--help-clear) - env(safe-area-inset-right)
    );
    grid-template-columns: minmax(var(--panel-min-w), 440px) var(--side);
    /* The small logo sits in the status row, right of the chips, so the words get its height. */
    grid-template-rows: calc(var(--status) - var(--pad)) minmax(0, 1fr);
    grid-template-areas: "mast frame" "panel frame";
    column-gap: var(--pad);
    align-content: start;
  }
}

/* ---------- mast ---------- */

.mast {
  grid-area: mast;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}

.logo {
  display: block;
  height: calc(var(--logo-h) - 6px);
  width: auto;
}

/* Sideways the small logo stands in the gutter under the help button, clear of the status chips. */
@media (orientation: landscape) {
  .mast {
    position: fixed;
    right: env(safe-area-inset-right);
    bottom: var(--in-b);
    width: var(--help-clear);
    padding: 0 6px;
  }
  .logo { width: 100%; height: auto; }
}

/* ---------- the rug ---------- */

.frame {
  grid-area: frame;
  justify-self: center;
  position: relative;
  width: var(--side);
  height: var(--side);
}

.rug {
  display: grid;
  grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
  grid-template-rows: repeat(var(--n, 3), minmax(0, 1fr));
  gap: var(--seam);
  width: 100%;
  height: 100%;
  padding: 0;
  background: var(--ak-outline);
  outline: var(--seam) solid var(--ak-outline);
  transition: opacity 220ms linear;
}

.rug.is-gone { opacity: 0; }

.tile {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  /* Pale linen with the faintest weave. */
  background-color: var(--ak-ink);
  background-image:
    repeating-linear-gradient(0deg, color-mix(in srgb, var(--ak-ink-muted) 10%, transparent) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(90deg, color-mix(in srgb, var(--ak-ink-muted) 10%, transparent) 0 1px, transparent 1px 4px);
  color: var(--ak-bg);
  cursor: pointer;
  touch-action: manipulation;
}

.tile:focus-visible {
  outline: 3px solid var(--ak-focus);
  outline-offset: -3px;
  z-index: 1;
}

/* A tile a miss showed to be whole: dimmed under a violet frame for the rest of the rug. */
.tile.is-cleared {
  background-color: color-mix(in srgb, var(--ak-ink) 55%, var(--ak-accent-2));
  box-shadow: inset 0 0 0 4px var(--ak-accent-2);
}

.tile.is-cleared .motif { opacity: 0.6; }

.tile.is-found { box-shadow: inset 0 0 0 5px var(--ak-accent), inset 0 0 0 7px var(--ak-bg); z-index: 1; }

.motif {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.st { stroke: currentColor; stroke-linecap: butt; fill: none; }
.knot { fill: currentColor; }
.ghost line { fill: none; stroke-linecap: butt; }
.gh-edge { stroke: var(--ak-bg); stroke-width: calc(var(--sw, 4.4) + 3); stroke-dasharray: 5 2; stroke-dashoffset: 0.5; }
.gh { stroke: var(--ak-accent); stroke-width: var(--sw, 4.4); stroke-dasharray: 4 3; }
.mark-edge { fill: none; stroke: var(--ak-bg); stroke-width: 4.4; }
.mark { fill: none; stroke: var(--ak-accent); stroke-width: 2.2; }
.sew-edge { stroke: var(--ak-bg); stroke-width: calc(var(--sw, 4.4) + 3); stroke-dasharray: 5 2; stroke-dashoffset: 0.5; stroke-linecap: butt; }
.sew { stroke: var(--ak-accent); stroke-width: var(--sw, 4.4); stroke-dasharray: 4 3; stroke-linecap: butt; }
.sewn { stroke: currentColor; stroke-width: var(--sw, 4.4); stroke-linecap: butt; }

/* ---------- teaching rings ---------- */

/* Steady frames, never fading: lime with a dark edge either side, so they read on linen and on violet. */
.guide-ring,
.hint-ring {
  --tile: calc((100% - (var(--n) - 1) * var(--seam)) / var(--n));
  position: absolute;
  z-index: 2;
  pointer-events: none;
  border: 4px solid var(--ak-accent);
  box-shadow: 0 0 0 2px var(--ak-bg), inset 0 0 0 1px var(--ak-bg);
}

/* Around the row, just outside it, so nothing it points at is covered. */
.guide-ring {
  left: -5px;
  right: -5px;
  top: calc(var(--row) * (var(--tile) + var(--seam)) - 5px);
  height: calc(var(--tile) + 10px);
}

/* Around the tile, on the seam and the dimmed neighbours, never on the tile's own stitches. */
.hint-ring {
  left: calc(var(--col) * (var(--tile) + var(--seam)) - 6px);
  top: calc(var(--row) * (var(--tile) + var(--seam)) - 6px);
  width: calc(var(--tile) + 12px);
  height: calc(var(--tile) + 12px);
  border-width: 5px;
}

/* While a hint is up, every tile outside it dims, so the lit one is the only bright thing on the rug.
   The lit tile also carries a thin lime edge of its own, in case the frame meets the screen's edge. */
.rug.is-hinting .tile:not(.is-lit) { opacity: 0.42; }
.tile.is-hint { box-shadow: inset 0 0 0 3px var(--ak-accent); z-index: 1; }

/* ---------- the focus pull: the loose tile, large, over the spot it was found ---------- */

/* The rest of the rug steps back but stays sharp; the found tile keeps its lime frame and new stitch. */
.rug.is-pulled .tile:not(.is-found) { opacity: 0.55; }

/* The loupe takes the level number's place in the panel. Its tile is as big as the panel allows
   beside the Next button: the hoop is 1/0.56 of the tile wide and 384/359 of that tall. */
.loupe {
  grid-column: 1;
  grid-row: 1;
  align-self: end;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  pointer-events: none;
  --big: clamp(44px, min((100cqw - var(--next-w) - 22px) / 2.79, (100cqh - 28px) / 1.92), 200px);
  --hoop: calc(var(--big) / 0.56);
}

.pair {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin: 0;
}

.pair figcaption {
  padding: 3px 8px;
  border: 1px solid var(--ak-ink);
  border-radius: 2px;
  background: var(--ak-surface);
  color: var(--ak-ink);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.hoop {
  position: relative;
  width: var(--hoop);
  aspect-ratio: 359 / 384;
}

.hoop-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The twin sits in a box the hoop's height, so both tiles and both captions line up. */
.hoop.is-bare {
  width: var(--big);
  aspect-ratio: auto;
  height: calc(var(--hoop) * 384 / 359);
}

.hoop .big-tile {
  position: absolute;
  left: 50%;
  top: 52%;
  translate: -50% -50%;
}

.big-tile {
  width: var(--big);
  height: var(--big);
  background-color: var(--ak-ink);
  color: var(--ak-bg);
}

.hoop.is-bare .big-tile { box-shadow: 0 0 0 1px var(--ak-ink), 0 0 0 4px var(--ak-bg); }

.found-streak {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ak-ink);
}

.found-streak span { display: block; }
.found-streak.is-new { padding-left: 8px; border-left: 3px solid var(--ak-accent); }

/* ---------- the level number coming up in the tray ---------- */

.tray {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.tray span {
  font-family: var(--ak-font-display);
  font-weight: var(--ak-title-weight, 700);
  font-size: min(33dvh, calc(var(--side) * 0.62));
  line-height: 1;
  color: var(--ak-accent);
  font-variant-numeric: tabular-nums;
}

/* ---------- the panel: one clue line, then the corner button (and, after a find, the loupe) ---------- */

.panel {
  grid-area: panel;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  row-gap: 8px;
  min-height: 0;
  padding: 10px var(--pad) 0;
}

/* Each part keeps its own row, so a hidden streak line never hands its row to the loupe. */
.clue { grid-row: 1; }
.found-streak { grid-row: 2; }
.result { grid-row: 3; }

/* The clue: a small drawing of the gap to look for, and one line beside it. */
.clue {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: 12px;
}

/* What to look for: a strip of linen with the kind of gap drawn as its lime ghost. */
.gap-key-strip {
  display: block;
  width: 72px;
  height: 34px;
  margin-top: 2px;
  background-color: var(--ak-ink);
  color: var(--ak-bg);
  box-shadow: 0 0 0 1px var(--ak-outline);
}

.gap-key-art { display: block; width: 100%; height: 100%; }

.say {
  margin: 0;
  font-size: 18px;
  line-height: 1.4;
  color: var(--ak-ink);
}

.say:empty { display: none; }

.say.is-wrong { padding-left: 10px; border-left: 4px solid var(--ak-accent-2); }

.say strong { font-weight: 700; }

/* After a find the drawing steps aside and "Found it." stands as a headline over the one sentence. */
.panel.is-found .clue { grid-template-columns: minmax(0, 1fr); }
.panel.is-found .gap-key-strip { display: none; }
.panel.is-found .say strong {
  display: block;
  margin-bottom: 2px;
  font-family: var(--ak-font-display);
  font-size: 22px;
  line-height: 1.2;
  color: var(--ak-accent);
}

/* The loupe (after a find) on the left, the one corner button on the right, both on the bottom edge. */
.result {
  --next-w: 150px;
  container-type: size;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: 12px;
  min-height: 0;
}

.side {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

/* A tall panel (upright phones) has more room above than beside: the loupe takes the full width and
   the Next button drops under it. */
@container (min-height: 240px) {
  .loupe {
    grid-column: 1 / -1;
    --big: clamp(44px, min((100cqw - 10px) / 2.79, (100cqh - 28px - 60px) / 1.92), 200px);
  }
  .panel.is-found .side { grid-row: 2; grid-column: 2; }
}

/* A short panel has no room for the hoop: the two tiles sit bare side by side, the loose one edged
   in lime, so the comparison still fits beside the Next button. */
@container (max-height: 150px) {
  .loupe { --big: clamp(36px, min((100cqw - var(--next-w) - 22px) / 2, 100cqh - 26px), 200px); }
  .hoop-art { display: none; }
  .hoop,
  .hoop.is-bare { width: var(--big); height: var(--big); aspect-ratio: auto; }
  .hoop .big-tile { top: 50%; }
  #loupe-loose { box-shadow: 0 0 0 3px var(--ak-accent), 0 0 0 5px var(--ak-bg); }
}

/* A short upright phone: the words step down a size so the button always stays on screen. */
@media (orientation: portrait) and (max-height: 700px) {
  .say { font-size: 16px; line-height: 1.35; }
  /* The headline runs into its sentence here, so the loupe and the button keep their room. */
  .panel.is-found .say strong { display: inline; font-size: 17px; }
  .found-streak { font-size: 11px; }
  .gap-key-strip { width: 63px; height: 30px; }
}

/* Sideways the words sit under the status chips, left of the rug, and everything stacks down the
   column: the line, the loupe, then the button across the full width at the bottom. */
@media (orientation: landscape) {
  .panel { padding: 10px 0 0; }
  .say { font-size: 18px; }
  /* --next-h is the button's row and its gap, which the loupe's height has to leave free. */
  .result { --next-h: 62px; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; row-gap: 10px; }
  .side { grid-column: 1; grid-row: 2; align-items: flex-start; }
  .next { align-self: stretch; }
  .loupe {
    grid-column: 1;
    grid-row: 1;
    --big: clamp(44px, min((100cqw - 10px) / 2.79, (100cqh - var(--next-h) - 28px) / 1.92), 220px);
  }
  /* Where the hoop would shrink the tiles, they sit bare and large side by side instead. */
  @container (max-height: 300px) {
    .loupe { --big: clamp(36px, min((100cqw - 14px) / 2, 100cqh - var(--next-h) - 30px), 220px); }
    .hoop-art { display: none; }
    .hoop,
    .hoop.is-bare { width: var(--big); height: var(--big); aspect-ratio: auto; }
    .hoop .big-tile { top: 50%; }
    #loupe-loose { box-shadow: 0 0 0 3px var(--ak-accent), 0 0 0 5px var(--ak-bg); }
  }
}

@media (orientation: landscape) and (max-height: 430px) {
  .say { font-size: 16px; line-height: 1.35; }
  .panel.is-found .say strong { font-size: 19px; }
  .gap-key-strip { width: 63px; height: 30px; }
}

.hint,
.next {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 52px;
  min-width: 52px;
  margin: 0;
  padding: 0 14px 0 5px;
  border: 1px solid var(--ak-ink);
  border-radius: 2px;
  background: var(--ak-surface);
  color: var(--ak-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  touch-action: manipulation;
}

/* The corner button while looking: the painted lens and its word. Always free. */
.hint {
  padding: 4px 14px 4px 4px;
}

.hint img {
  width: 40px;
  height: 40px;
}

/* After a find it is the next thing to press, so it wears the one accent, filled. */
.next {
  min-width: var(--next-w, 150px);
  justify-content: space-between;
  padding: 0 12px 0 16px;
  border-color: var(--ak-accent);
  background: var(--ak-btn-fill);
  color: var(--ak-btn-ink);
}

.next svg { width: 18px; height: 18px; }

.hint:focus-visible,
.next:focus-visible {
  outline: 3px solid var(--ak-focus);
  outline-offset: 2px;
}

/* ---------- the poster: the result takes the whole screen ---------- */

.poster {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: grid;
  align-content: start;
  grid-template-areas: "reason" "count" "strip" "score";
  gap: 10px;
  padding: calc(var(--status) + 4px) var(--in-r) var(--in-b) var(--in-l);
  background-color: var(--ak-bg);
  background-image: var(--ak-backdrop-layer, var(--ak-bg-art));
  background-size: cover;
  background-position: center;
}

.poster-count p { margin: 0; }

.poster-reason {
  grid-area: reason;
  margin: 0;
  font-family: var(--ak-font-display);
  font-weight: var(--ak-title-weight, 700);
  font-size: 20px;
  color: var(--ak-ink);
}

.poster-number {
  font-family: var(--ak-font-display);
  font-weight: var(--ak-title-weight, 700);
  font-size: min(15dvh, 36dvw);
  line-height: 0.86;
  letter-spacing: -0.04em;
  color: var(--ak-accent);
  font-variant-numeric: tabular-nums;
}

.poster-count { grid-area: count; }
.strip { grid-area: strip; }
.poster-score { grid-area: score; }
.poster-score p { margin: 0; }

.poster-best {
  margin-top: 4px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ak-ink-muted);
}

.poster-best.is-new { color: var(--ak-accent); }

/* Marks for clean streaks: a kept mark is a solid frame with lime stitches, one still to earn a dashed outline. */
.marks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.mark-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ak-ink-muted);
}

.mark-item svg { width: 28px; height: 28px; flex: none; }

/* Upright there is room for one line under the strip: the marks carry the streak story. */
@media (orientation: portrait) {
  #poster-streak { display: none; }
}
.mark-item .mark-frame { fill: none; stroke: var(--ak-ink-muted); stroke-width: 1.5; stroke-dasharray: 4 3; }
.mark-item path { fill: none; stroke: var(--ak-ink-muted); stroke-width: 3; }
.mark-item.is-kept { color: var(--ak-ink); }
.mark-item.is-kept .mark-frame { fill: var(--ak-surface); stroke: var(--ak-ink); stroke-dasharray: none; }
.mark-item.is-kept path { stroke: var(--ak-accent); }

.poster-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ak-ink);
}

.strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--seam);
  width: min(100%, 520px);
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--ak-outline);
  outline: var(--seam) solid var(--ak-outline);
}

.strip li {
  aspect-ratio: 1;
  background-color: var(--ak-ink);
  color: var(--ak-bg);
}

/* Sideways: the reason sits top-left above the chrome's verdict, which lands bottom-left; the big
   count, the ten loose tiles, the scores to beat and the marks fill the right half. */
@media (orientation: landscape) {
  .poster {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: ". count" ". strip" ". score";
    gap: 8px 28px;
    align-items: start;
    padding-top: var(--pad);
    padding-right: calc(var(--help-clear) + env(safe-area-inset-right));
  }
  /* The chrome's verdict card on the left already says "All ten found.", so it is said once. */
  .poster-reason { display: none; }
  .mark-item { font-size: 12px; }
  .poster-number { font-size: min(20dvh, 14dvw); }
  .poster-label { font-size: 14px; }
  .poster-best { font-size: 14px; letter-spacing: 0.12em; }
  .strip { width: min(100%, 52dvh * 2.5, 520px); }

  /* The chrome's verdict card, sized up to read at arm's length and kept to the left half. */
  html body #activity-chrome-ending.site-expercepth .ac-card { max-width: min(420px, calc(50vw - 56px)); }
  html body #activity-chrome-ending.site-expercepth .ac-verdict { font-size: 21px; }
  html body #activity-chrome-ending.site-expercepth .ac-lines { font-size: 16px; line-height: 1.4; }
  html body #activity-chrome-ending.site-expercepth .ac-actions button {
    min-height: 52px;
    padding: 0 16px;
    font-size: 14px;
    letter-spacing: 0.12em;
  }
}

/* A short sideways phone: the streak line and mark names give way so everything fits. */
@media (orientation: landscape) and (max-height: 400px) {
  .poster { gap: 6px 28px; }
  .poster-number { font-size: 18dvh; }
  #poster-streak { display: none; }
  .marks { margin-top: 4px; }
}

/* A short upright phone: the marks show as shapes only, so the verdict below has room. */
@media (orientation: portrait) and (max-height: 700px) {
  .mark-item span { display: none; }
  .poster-number { font-size: min(13dvh, 36dvw); }
}

/* ---------- the opening, in this rug's cut ---------- */

/* start.js draws the opening and owns its words and order. These lines only size its parts for this
   game: the title poster-sized in the middle, and the painted lens as the one thing to press.
   The doubled id and html body outrank start.js's own short-screen rules, which load after this sheet. */
html body #activity-start#activity-start.site-expercepth .ak-logo {
  width: auto;
  max-width: min(92vw, 620px);
  max-height: min(34dvh, 280px);
}

html body #activity-start#activity-start.site-expercepth.is-instructions .ak-logo { max-height: min(24dvh, 200px); }

/* The lens: the painted magnifier on the dot field, its word underneath in plain ink. */
html body #activity-start#activity-start.site-expercepth .ak-go,
html body #activity-start#activity-start.site-expercepth .ak-go:hover,
html body #activity-start#activity-start.site-expercepth .ak-go:active {
  width: 176px;
  height: auto;
  min-width: 0;
  min-height: 176px;
  padding: 150px 0 0;
  border: 0;
  border-radius: 0;
  background: url("./art/lens-button.png") center top / 144px 144px no-repeat;
  box-shadow: none;
  color: var(--ak-ink);
  font-size: 13px;
  line-height: 1.3;
  letter-spacing: 0.24em;
}

html body #activity-start#activity-start.site-expercepth .ak-go:active { transform: scale(0.97); }

/* Sideways the opening runs side by side: the title on the left, the lines and the lens on the right. */
@media (orientation: landscape) and (max-height: 500px) {
  html body #activity-start#activity-start.site-expercepth .ak-scene {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "head lines" "head actions";
    align-content: center;
    align-items: center;
    column-gap: 32px;
    row-gap: 12px;
    width: min(100%, 980px);
  }
  html body #activity-start#activity-start.site-expercepth .ak-scene .ak-head { grid-area: head; }
  html body #activity-start#activity-start.site-expercepth .ak-scene-lines { grid-area: lines; gap: 8px; }
  html body #activity-start#activity-start.site-expercepth .ak-scene-actions { grid-area: actions; }
  html body #activity-start#activity-start.site-expercepth .ak-scene-line { font-size: clamp(16px, 2.4vw, 20px); line-height: 1.3; }
  html body #activity-start#activity-start.site-expercepth .ak-scene .ak-logo { max-width: 44vw; max-height: 62dvh; }
  html body #activity-start#activity-start.site-expercepth .ak-scene { min-height: calc(100dvh - 48px); }
  html body #activity-start#activity-start.site-expercepth .ak-go,
  html body #activity-start#activity-start.site-expercepth .ak-go:hover,
  html body #activity-start#activity-start.site-expercepth .ak-go:active {
    width: 160px;
    height: auto;
    min-height: 116px;
    margin: 0;
    padding-top: 98px;
    background-size: 92px 92px;
  }
}

@media (prefers-reduced-motion: no-preference) {
  /* A slow breath, so the lens reads as the thing to press. */
  html body #activity-start#activity-start.site-expercepth .ak-go { animation: lens-breathe 2400ms ease-in-out 1200ms infinite; }
  @keyframes lens-breathe {
    0%, 100% { scale: 1; }
    50% { scale: 1.04; }
  }
}

/* ---------- motion ---------- */

@media (prefers-reduced-motion: no-preference) {
  :root:not(.ac-still) .tile.is-pulse { animation: pulse 420ms cubic-bezier(0.22, 1, 0.36, 1); }
  :root:not(.ac-still) .tile.is-found { animation: settle 260ms cubic-bezier(0.22, 1, 0.36, 1); }
  /* The frames land with one squeeze, then hold still at full strength. */
  :root:not(.ac-still) .guide-ring.is-flash,
  :root:not(.ac-still) .hint-ring.is-flash { animation: ring 520ms cubic-bezier(0.22, 1, 0.36, 1); }
  :root:not(.ac-still) .rug .tile { transition: opacity 240ms ease-out; }
  /* Teaching points with a slow breath of the frame; it never fades. */
  :root:not(.ac-still) .guide-ring.is-teach { animation: breathe 1400ms ease-in-out infinite; }
  :root:not(.ac-still) .loupe:not([hidden]) .pair { animation: focus-in 420ms ease-out both; }
  :root:not(.ac-still) .loupe:not([hidden]) .pair + .pair { animation-delay: 140ms; }
  :root:not(.ac-still) .say.is-wrong { animation: nudge 260ms ease-out; }
  :root:not(.ac-still) .tray.is-up span { animation: rise-fade 720ms cubic-bezier(0.22, 1, 0.36, 1) both; }
  :root:not(.ac-still) .rug.is-develop { animation: develop 720ms ease-out both; }
  :root:not(.ac-still) .poster:not([hidden]) .poster-count { animation: focus-in 600ms ease-out both; }
  :root:not(.ac-still) .poster:not([hidden]) .strip li { animation: focus-in 520ms ease-out both; animation-delay: calc(500ms + var(--i) * 120ms); }
  :root:not(.ac-still) .panel.is-fading,
  :root:not(.ac-still) .mast.is-fading { opacity: 0; transition: opacity 500ms linear; }

  @keyframes pulse {
    0% { transform: scale(1); }
    40% { transform: scale(0.92); }
    100% { transform: scale(1); }
  }
  @keyframes settle {
    0% { transform: scale(1.06); }
    100% { transform: scale(1); }
  }
  @keyframes nudge {
    0% { transform: translateX(0); }
    35% { transform: translateX(6px); }
    100% { transform: translateX(0); }
  }
  @keyframes ring {
    0% { scale: 1.25; }
    100% { scale: 1; }
  }
  @keyframes breathe {
    0%, 100% { scale: 1; }
    50% { scale: 1.025; }
  }
  @keyframes focus-in {
    from { opacity: 0.2; filter: blur(8px); }
    to { opacity: 1; filter: blur(0); }
  }
  @keyframes rise-fade {
    0% { opacity: 0; transform: translateY(18%); filter: blur(6px); }
    40% { opacity: 1; transform: none; filter: blur(0); }
    100% { opacity: 0; transform: none; }
  }
  @keyframes develop {
    from { opacity: 0.35; filter: contrast(0.2) brightness(1.5); }
    to { opacity: 1; filter: none; }
  }
}
