/* The app's screens, on top of style.css (the loom, the target, Check): the same paper and ink,
   nothing more (docs/art/style.md, How UI looks). Sizes are for a 390 px wide phone and up. */

/* The iPhone's status bar, Dynamic Island and home indicator (037): with viewport-fit=cover the
   paper (on :root) fills the whole screen, and the page sits inside the safe-area insets, the
   body's own padding (style.css, its narrow-screen rule included) untouched within them. The
   scroll that brings Share, Next and Home into view on the solve stops short of the insets too.
   Where there is no notch every inset is 0, so nothing moves. */
html {
  /* 064: exactly the screen's height (with box-sizing, the insets inside it), so the puzzle screen's
     body can fill what's left; and no rubber-band at the page's edges. */
  height: 100%;
  overscroll-behavior: none;
  padding:
    env(safe-area-inset-top, 0px)
    env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px)
    env(safe-area-inset-left, 0px);
  scroll-padding:
    env(safe-area-inset-top, 0px)
    env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px)
    env(safe-area-inset-left, 0px);
}

/* The header's backdrop (048): a strip of paper under the status bar that stays put, so a page
   scrolled by the solve never draws under the clock or the Dynamic Island. */
html::before {
  content: "";
  position: fixed;
  z-index: 10;
  top: 0;
  left: 0;
  right: 0;
  height: env(safe-area-inset-top, 0px);
  background: var(--paper);
}

/* Texture per colour (047, texture.ts), its tiles starting where a strip meets the next crossing. */
.edge .band, .crossing .band, #target .cell, .swatch { background-image: var(--p, none); background-size: var(--ps, auto); }
.edge.row .band { background-position: right top; }
.edge.col .band { background-position: right bottom; }
.crossing .v { background-position: right top; }

/* `hidden` wins over the display rules below (the Home link, the after-solve buttons). */
[hidden] { display: none !important; }

/* Links are Thread, never underlined; Home in the header has a back arrow and a full tap area. */
a.link {
  color: var(--thread);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  min-width: 44px;
}
#home-link { padding: 0 4px; font-weight: 600; }
#home-link .arrow { font-size: 1.4rem; line-height: 1; }

/* Home: four big choices, cards of Panel with a Rule hairline. */
.choice {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 12px;
  margin: 12px 0;
  padding: 14px 16px;
  min-height: 64px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
}
.choice .name { font-size: 1.2rem; font-weight: 600; }
.choice .detail { grid-column: 1; color: var(--muted); }
.choice .state { grid-column: 2; grid-row: 1 / span 2; }
.choice.none { border-style: dashed; }
/* Today with its tile: the weave first, then the words, then the tag. */
.choice.with-tile { grid-template-columns: auto 1fr auto; }
.choice.with-tile .tile { grid-column: 1; grid-row: 1 / span 2; }
.choice.with-tile .name, .choice.with-tile .detail { grid-column: 2; }
.choice.with-tile .state { grid-column: 3; }

/* A puzzle's woven tile, drawn in code (tile.ts); a quiet linen square with a ? until it's solved. */
.tile { display: block; width: 56px; height: 56px; border-radius: 4px; background: var(--linen); }
.tile.blank { display: grid; place-items: center; color: var(--muted); font-size: 1.4rem; font-weight: 600; }
.tile.blank::after { content: "?"; }
.list .tile, .list .slot { width: 36px; height: 36px; flex: none; }

/* Archive and levels: one row each. */
.list { list-style: none; margin: 0; padding: 0; }
.list .row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  text-decoration: none;
}
.list .row .detail { flex: 1; }
.list .empty { padding: 12px 0; }
.count { color: var(--muted); font-size: 0.95rem; white-space: nowrap; }
.note { color: var(--muted); margin: 0 0 8px; }

/* Tags: small pills. "new" in Thread outline, "locked" in Rule dashed with a lock, "solved" in Solved fill. */
.state {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.85rem;
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--muted);
  white-space: nowrap;
}
.state.new { border-color: var(--thread); color: var(--thread); }
.state.started { border-color: var(--muted); color: var(--ink); }
.state.solved { border-color: var(--good); background: var(--good); color: #fff; }
.state.locked { border-style: dashed; }
.state .lock { width: 10px; height: 12px; }

/* A link that looks like the page's buttons: an Ink outline, or (one per screen) an Ink fill. */
a.button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 8px 18px;
  border-radius: 8px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  text-decoration: none;
}
a.button.primary { background: var(--ink); color: var(--paper); }

/* A level's puzzles by number. */
.numbers {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 4px;
  margin: 16px 0;
}
.numbers .number {
  display: grid;
  place-items: center;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
}
.numbers .number[data-state="started"] { border: 2px solid var(--ink); }
.numbers .number[data-state="solved"] { background: var(--good); border-color: var(--good); color: #fff; }

/* Levels and Stats (065): objects, not rows of words. Two text sizes on each (1rem, and 0.85rem for
   counts and labels) besides the titles and Stats' big numbers; Thread the one accent (today's day);
   bars and notches in Ink on Rule, so nothing reads by colour alone. */

/* A thin progress bar: a Rule track, filled in Ink (.meter, not .bar: that is the puzzle's action bar). */
.meter { display: block; height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
.meter .fill { display: block; height: 100%; background: var(--ink); }

/* Levels: one card per level, its tile on the left; the weekday and its notches, then the bar and the count. */
.cards { list-style: none; margin: 0; padding: 0; }
.level-card {
  display: grid;
  grid-template-columns: 56px 1fr;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 8px 14px;
  margin: 10px 0;
  padding: 12px 14px;
  min-height: 64px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.level-card .tile { grid-row: 1 / span 2; box-shadow: 0 0 0 1px var(--line); }
.level-card .head, .level-card .foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
.level-card .name { font-weight: 600; }
.level-card .foot .meter { flex: 1; }
.level-card .count { color: var(--muted); font-size: 0.85rem; white-space: nowrap; }
.level-card .foot .state { margin-left: auto; }

/* Difficulty: seven small notches, the first n filled in Ink, the rest hollow in Rule (not .peg:
   that is the loom's pin). */
.notches { display: inline-flex; gap: 3px; flex: none; }
.notch { display: block; width: 6px; height: 12px; border-radius: 1px; border: 1px solid var(--line); }
.notch.on { background: var(--ink); border-color: var(--ink); }

/* Stats: three big numbers, the last week as tiles, the levels as compact rows. */
#stats h3 { margin: 20px 0 8px; font-size: 1rem; font-weight: 600; }
.bigs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 12px 0 0; }
.big {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 12px 4px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
}
.big .value { font-size: 2rem; font-weight: 600; line-height: 1.1; }
.big .label { color: var(--muted); font-size: 0.85rem; text-align: center; }

.week { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.week .day { display: grid; place-items: center; }
.week .tile { width: 100%; max-width: 40px; height: auto; aspect-ratio: 1; }
.week .tile.quiet { display: grid; place-items: center; color: var(--muted); font-size: 0.85rem; }
.week .day[data-state="none"] .tile { background: transparent; box-shadow: inset 0 0 0 1px var(--line); }
.week .day.today .tile { box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--thread); }

/* One grid for the seven rows (each row a subgrid), so every bar is the same length. */
.level-rows { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr auto; }
.level-stats {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  gap: 4px 12px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--line);
}
.level-stats .name { font-weight: 600; }
.level-stats .solved { font-size: 0.85rem; white-space: nowrap; }
/* Two lines: the name and the count; the bar, and beside it the median and no-Check count (a dash for none). */
.level-stats .name { grid-column: 1; grid-row: 1; }
.level-stats .solved { grid-column: 2; grid-row: 1; text-align: right; }
.level-stats .meter { grid-column: 1; grid-row: 2; }
.level-stats .detail { grid-column: 2; grid-row: 2; color: var(--muted); font-size: 0.85rem; text-align: right; white-space: nowrap; }
.level-stats.none .solved, .level-stats.none .name { color: var(--muted); }
#stats-overall { margin: 12px 0 0; font-size: 0.85rem; }

/* One screen for Levels and Stats too (072), as the puzzle (064): the page is the screen's height inside
   the safe-area insets (html's height and padding, above), and the seven level cards, or Stats' seven
   level rows, share what's left after the titles and the other stats, each no taller than 065 drew it
   (82 px cards, 62 px rows) and no shorter than its content. Where even that can't fit (320 × 568), the
   page scrolls as before, and the spacer (as the puzzle's) keeps its end above the home indicator. */
body:has(> main > :is(#levels, #stats):not([hidden])) {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding-top: 8px;
  /* 8 px, or 24 above a home indicator (065: the screen's end clear of it by 24): env × 100 is 0 with
     no indicator and past 24 with one. */
  padding-bottom: max(8px, min(24px, calc(env(safe-area-inset-bottom, 0px) * 100)));
}
body:has(> main > :is(#levels, #stats):not([hidden]))::after {
  content: "";
  flex: none;
  height: env(safe-area-inset-bottom, 0px);
  margin-bottom: calc(-1 * env(safe-area-inset-bottom, 0px));
}
body:has(> main > :is(#levels, #stats):not([hidden])) > header { margin-bottom: 0; }
body:has(> main > :is(#levels, #stats):not([hidden])) > main { flex: 1; display: flex; flex-direction: column; }
:is(#levels, #stats):not([hidden]) { flex: 1; display: flex; flex-direction: column; }
:is(#levels, #stats) > h2 { margin: 4px 0 6px; }

/* Levels: each card's slot is a size container, so the card fills it and its tile is the card's height
   less its padding: 56 px as 065 drew it where there's room, down to 40 on an iPhone SE. */
#levels .note { margin-bottom: 4px; }
#level-list { flex: 1; display: flex; flex-direction: column; }
#level-list > li { --tile: min(56px, calc(100cqh - 18px)); flex: 1 1 0; min-height: 56px; max-height: 88px; container-type: size; padding: 3px 0; }
#level-list .level-card { height: 100%; min-height: 0; margin: 0; padding: 0 14px; gap: 4px 14px; align-content: center; grid-template-columns: var(--tile) 1fr; }
#level-list .level-card .tile { width: var(--tile); height: var(--tile); }

/* Stats: the gaps between its parts tighter than 065's, and the seven level rows share what's left,
   each between its content's height and 065's 62 px (one grid, so every bar stays the same length). */
#stats h3 { margin: 10px 0 4px; }
#stats .bigs { margin-top: 4px; }
#stats .big { padding: 6px 4px 4px; }
#stats .level-rows { flex: 1 1 0; grid-auto-rows: minmax(auto, 1fr); max-height: calc(7 * 62px); }
#stats .level-stats { align-content: center; padding: 2px 4px; line-height: 1.25; }
#stats-overall { margin-top: 6px; }

/* The bar at the bottom (064), one row: Check, and once solved Share beside it. The next step (Next
   puzzle, Home) is a row of its own below. */
.actions { display: flex; gap: 8px 12px; align-items: flex-start; flex-wrap: wrap; margin: 0; }
.next-row { display: flex; gap: 12px; align-items: center; flex-basis: 100%; }
.next-row p { margin: 0; color: var(--muted); }

/* Beside the picture, under the key (062, 064): Show letters, and Rules, so Rules is in view
   whenever the loom is. */
.tools { display: flex; align-items: center; flex-wrap: wrap; gap: 0 6px; }
button.small { padding: 4px 12px; }
/* No light-blue tap flash: the browser's default on a phone, which reads as a stray box. */
.tools button, dialog.sheet button { -webkit-tap-highlight-color: transparent; }

/* The rules sheet (062): a Panel card over a dimmed page, the rules in Ink, Close below them. */
dialog.sheet {
  /* A modal dialog is in the top layer, fixed to the viewport by the browser: % is of the screen. */
  width: min(480px, calc(100% - 24px));
  max-width: none;
  max-height: calc(100% - 24px);
  margin: auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
}
dialog.sheet::backdrop { background: rgba(34, 34, 34, 0.45); }
.sheet-inner { padding: 16px 16px 16px; }
.sheet-inner h2 { margin: 0 0 8px; }
.sheet-inner .rules { margin: 0 0 16px; color: var(--ink); font-size: 1rem; }
.sheet-inner .rules li { margin-bottom: 8px; }

/* Share stays beside Check; Save image and Copy text open full width below it. */
.after, .share { display: contents; }
.share-fallback { display: flex; gap: 8px; flex-wrap: wrap; flex-basis: 100%; }
.share-fallback button {
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
#share-note { flex-basis: 100%; margin: 0; color: var(--muted); overflow-wrap: anywhere; }
#share-note:empty { display: none; }

/* One screen (064). On the puzzle screen the page is the screen's height inside the safe-area insets
   (html's height, above, with its padding inside it): the header, the title, the picture with its
   key, the loom, the Check message, and the bar pushed to the bottom (in the flow, so it never covers
   the loom or the message). Nothing scrolls while it fits; where it can't (320 × 568), the page
   scrolls as before rather than hide a control. No screen bounces or rubber-bands at its edges
   (overscroll-behavior: none on html, above, and on the body), in a browser or in the app's web view
   (WKWebView honours it on the root from iOS 16); lists (Archive, Levels, Stats) still scroll the page. */
body { overscroll-behavior: none; }
body:has(> main > #play:not([hidden])) {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding-top: 8px;
  padding-bottom: 0;
}
/* Where the screen can't fit and the page scrolls, html's bottom padding (fixed at the screen's
   bottom by its height) no longer follows the content: this spacer, the home indicator's height and
   taking no room in the flow, lets the scroll end with the bar above the indicator, as before 064. */
body:has(> main > #play:not([hidden]))::after {
  content: "";
  flex: none;
  height: env(safe-area-inset-bottom, 0px);
  margin-bottom: calc(-1 * env(safe-area-inset-bottom, 0px));
}
body:has(> main > #play:not([hidden])) > header { margin-bottom: 0; }
body:has(> main > #play:not([hidden])) > main { flex: 1; display: flex; flex-direction: column; }
#play:not([hidden]) { flex: 1; display: flex; flex-direction: column; }
#play > #title { margin: 0 0 4px; }
#play > .bar { position: relative; margin-top: auto; padding: 6px 0; }
#play > .bar .actions { row-gap: 6px; }

/* The loom takes the room that's left. html is the size container (its content box: the screen inside
   the insets, which media queries can't see), and a crossing is what's left of its height after the
   header (--head: the app's, with Home's 44 px tap area; the launch page sets its own), the title,
   the picture (--cell) and, below the loom, the edges, the message and the bar at their tallest (the
   solved bar's two rows), shared by six; or the width's share, if less; never under 44 px (the tap
   area) nor over 56. Where container units aren't supported, the sizes above stand (48 px). */
:root { --head: 44px; }
@supports (container-type: size) {
  html:has(#play:not([hidden])) { container-type: size; }
  #play {
    --target: calc(6 * var(--cell) + 20px);
    --cross: clamp(44px, min(calc((100cqh - 8px - var(--head) - 37px - var(--target) - 177px) / 6), calc((min(100cqw, 560px) - 62px) / 6)), 56px);
  }
}

/* The picture's cells: 20 px; 24 on a tall, wide phone (Pro Max); 16 on a short one (iPhone SE, and
   a phone browser with its toolbars), the key's swatches 20 there. The solved line a little smaller
   below 420 px, so it stays on one line. */
@media (min-height: 800px) and (min-width: 420px) {
  :root { --cell: 24px; }
}
@media (max-height: 760px) {
  :root { --cell: 16px; --swatch: 20px; }
  #legend { row-gap: 2px; }
}
@media (max-width: 419.98px) {
  #play.solved #status { font-size: 1.15rem; }
}

/* The first-run demo (066): the title, a sentence, the figure, a sentence and Got it, nothing else.
   The figure is a small loom drawn by the same rules as the real one (style.css): one strip across
   and three down on the linen, each running past the crossings at both ends, the words under it.
   Its crossings are pictures, not buttons. */
#demo { --cross: 56px; }
#demo p { margin: 8px 0; }
#demo-title { margin-top: 8px; }
.demo-figure { width: max-content; margin: 12px auto; }
.demo-loom, .demo-captions {
  display: grid;
  grid-template-columns: var(--edge) repeat(3, var(--cross)) var(--edge);
  padding: 0 6px;
}
.demo-loom { grid-template-rows: var(--edge) var(--cross) var(--edge); padding: 6px; background: var(--linen); border-radius: 4px; }
.demo-loom .crossing { cursor: default; }
.demo-captions .caption { text-align: center; color: var(--muted); font-size: 0.95rem; padding-top: 4px; }
#demo-done { display: block; width: 100%; margin: 16px 0 0; }

/* On the Rules sheet: the way back to the demo, quiet, in Thread, with a full tap area. */
.sheet-inner button.quiet {
  display: flex;
  align-items: center;
  min-height: 44px;
  margin: 0 0 8px;
  padding: 0 4px;
  border: none;
  background: none;
  color: var(--thread);
  cursor: pointer;
}
