/* ==========================================================================
   layout.css — app shell, header, word list.
   ========================================================================== */

/* Outside the scroller, so it cannot move with the list. */
.app-header {
  flex: none;
  z-index: 10;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  padding-top: var(--safe-top);
  padding-left: max(var(--page-pad), var(--safe-left));
  padding-right: max(var(--page-pad), var(--safe-right));
}

.app-header__bar {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 14px 0 12px;
}

.app-header__title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Inside a folder the name is also the way back. */
.app-header__title.is-tappable { cursor: pointer; }

/* Right-hand cluster of header controls. */
.app-header__tools {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  align-self: center;
}

/* Icon-only header control. */
.header-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-spring);
}

.header-action:active { transform: scale(0.9); }

/* Sits before the title rather than in the right-hand cluster. */
.header-action--back {
  margin-left: -6px;
  color: var(--text-dim);
}

.header-action--back svg { stroke-width: 2.2; }

.header-action svg {
  width: 19px;
  height: 19px;
  stroke-width: 1.9;
}

/* Sort control, pinned to the right of the title row. */
.sort {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  align-self: center;
  min-height: 34px;
  padding: 0 11px 0 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-spring);
}

.sort:active { transform: scale(0.94); }

.sort { box-shadow: inset 0 1px 0 var(--gloss-edge); }

.sort__icon {
  width: 15px;
  height: 15px;
  stroke-width: 2;
  flex: none;
}

.sort__label {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  /* Fixed width keeps the button from resizing as the label cycles. */
  min-width: 3.6em;
  text-align: left;
}

/* The only scrolling region on the home screen. */
.app-main {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Keeps a scroll that runs off the end from reaching the document. */
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  padding-left: max(var(--page-pad), var(--safe-left));
  padding-right: max(var(--page-pad), var(--safe-right));
  /* Leave room for the floating button plus the home indicator. */
  padding-bottom: calc(112px + var(--safe-bottom));
}

/* Held still while a dialog is open; see js/scroll-lock.js. */
.app-main.is-locked { overflow: hidden; }

/* --- Folder grid --------------------------------------------------------- */
/* Two columns with the right one dropped half a tile, so the rows interlock
   like brickwork. The offset is padding on the column rather than a transform
   on alternate tiles: a transform would leave the shorter column hanging and
   open a gap under the last row. */

.folders {
  /* Read by js/folder-grid.js, which builds this many columns. */
  --folder-columns: 2;
  display: flex;
  gap: 14px;
  padding: 8px 0 4px;
}

.folders__column {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Every other column drops, so any number of them still interlocks. */
.folders__column:nth-child(even) {
  padding-top: 38px;
}

.folder-tile {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 3px;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 22px;
  /* Same darker band as the photo tiles end in, so every caption sits on the
     same ground whether there is a photo or not. */
  background: linear-gradient(
    to bottom,
    var(--surface-raise) 52%,
    var(--tile-caption) 86%
  );
  color: var(--text);
  text-align: left;
  cursor: pointer;
  box-shadow: inset 0 1px 0 var(--gloss-edge);
  transition: transform var(--dur-fast) var(--ease-spring),
              border-color var(--dur-fast) var(--ease-out);
}

.folder-tile:active { transform: scale(0.96); }

/* A soft sheen off the top-left. Skipped where a photo already fills the
   tile, since it would only muddy the image. */
.folder-tile:not(.folder-tile--photo)::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(158deg, var(--gloss-sheen) 0%, transparent 46%);
  pointer-events: none;
}

.folder-tile__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The border is drawn on the tile; the photo must not paint over it. */
  border-radius: inherit;
}

/* Carries the photo into the flat background the caption sits on, so the text
   never lands on busy pixels. */
.folder-tile__veil {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    to bottom,
    var(--tile-fade) 24%,
    var(--tile-caption) 72%
  );
  pointer-events: none;
}

.folder-tile__name,
.folder-tile__count {
  position: relative;
}

.folder-tile--unsorted {
  border-style: dashed;
  color: var(--text-dim);
}

/* One line always. js/fit-text.js shrinks the type to fit; past the point
   where it would stop being readable the tail is elided instead, because a
   6px folder name is no more use than a clipped one. */
.folder-tile__name {
  font-size: 17px;
  font-weight: 650;
  line-height: 1.25;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.folder-tile__count {
  font-size: 13px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* --- Word list ----------------------------------------------------------- */
/* The home screen shows the word and nothing else. */

.word-list { padding-top: 4px; }

.word-list li { border-bottom: 1px solid var(--line); }

/* --- Swipe to archive ------------------------------------------------------
   The row is opaque and slides over a label that is already in place, so the
   action is uncovered rather than drawn — which is why nothing has to be
   inserted or measured mid-gesture. */

.word-item {
  position: relative;
  /* Kept off the compositor until a finger is actually on it. */
  overflow: hidden;
}

.word-item__action {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding-right: 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  opacity: var(--row-progress, 0);
  pointer-events: none;
}

.word-item__icon {
  width: 17px;
  height: 17px;
  stroke-width: 1.8;
}

/* Opaque, and positioned so it paints above the absolutely placed label
   rather than under it. */
.word-item .word-row {
  position: relative;
  background: var(--bg);
}

/* Under the finger: no transition at all, or the row trails behind it. The
   row already carries a 140ms transform transition for its press feedback. */
.word-item.is-swiping .word-row {
  transform: translateX(var(--row-x, 0px));
  transition: none;
}

/* Held open while the confirmation is on screen. */
.word-item.is-parked .word-row {
  transform: translateX(var(--row-x, 0px));
  transition: transform 190ms var(--ease-out);
}

/* Springing back, or leaving for good — the same movement either way, only
   the destination differs. */
.word-item.is-exiting .word-row {
  transform: translateX(var(--row-x, 0px));
  transition: transform 190ms var(--ease-out);
}

.word-item.is-parked .word-item__action,
.word-item.is-exiting .word-item__action {
  transition: opacity 190ms var(--ease-out);
}

/* Applied for a single frame while the gesture state is cleared, so the row
   snaps back to rest instead of transitioning there. */
.word-item.is-reset .word-row {
  transform: none !important;
  transition: none !important;
}

/* The gap closes after the row has left, so the list does not jump. */
.word-item.is-collapsing {
  transition: height 200ms var(--ease-out), opacity 200ms var(--ease-out);
  opacity: 0;
}

/* 11px padding with 17px type gives a ~48px row against the previous ~63px,
   which is the ~1.3x more words per screen this was asked to hit. */
.word-row {
  display: block;
  width: 100%;
  padding: 11px 4px;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-align: left;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}

.word-row:active { transform: scale(0.985); }

/* A word queued for upload is dimmed until the server confirms it. */
.word-row--pending { color: var(--text-dim); }

.word-row--pending::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 8px;
  border-radius: 50%;
  background: var(--text-faint);
  vertical-align: middle;
}

/* --- Empty states -------------------------------------------------------- */

.empty {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  padding: 96px 0;
  text-align: center;
}

.empty__title { font-size: 17px; font-weight: 600; color: var(--text-dim); }
.empty__hint  { font-size: 15px; color: var(--text-faint); }

/* --- Wider screens ------------------------------------------------------- */

@media (min-width: 700px) {
  .app-header__bar,
  .word-list,
  .folders,
  .empty {
    max-width: 640px;
    margin-inline: auto;
  }
  .app-header__title { font-size: 30px; }

  .folders__column:nth-child(even) { padding-top: 46px; }
}

/* --- Browser on a wide screen ----------------------------------------------
   iPad and Mac browsers get a layout that spreads out with the window. The
   installed app is left exactly as it is: it reports display-mode standalone,
   so none of this applies there, and an engine that does not understand the
   query at all falls back to the fixed layout rather than the wide one. */

@media (display-mode: browser) and (min-width: 700px) {
  :root {
    --page-pad: 28px;
    --content-max: 1180px;
  }

  .app-header__bar,
  .word-list,
  .folders,
  .empty {
    max-width: var(--content-max);
  }

  .folders {
    --folder-columns: 3;
    gap: 18px;
  }
  .folders__column { gap: 18px; }
  .folders__column:nth-child(even) { padding-top: 56px; }

  .folder-tile { border-radius: 24px; }
  .folder-tile__name { font-size: 18px; }

  /* Row-major cells rather than newspaper columns: in a list that scrolls, a
     reader should never have to go back up to find where the next column
     starts. Cells in a row stretch to the same height, so the rules under
     them line up across the page. */
  .word-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    column-gap: 36px;
  }

  .word-row { overflow-wrap: anywhere; }
}

@media (display-mode: browser) and (min-width: 960px) {
  .folders { --folder-columns: 4; }
}

@media (display-mode: browser) and (min-width: 1280px) {
  .folders { --folder-columns: 5; }
}

/* Rows sliding into the cells they move to after one leaves; see
   js/list.js. Only ever set in the multi-column layout above. */
.word-item.is-reflowing {
  transition: transform 260ms var(--ease-out);
}
