/* THE ALL LAUNCHER. #apps composes .app-scroller-page like every document-shaped pane;
   what follows is its own grid, tile and pager -- the reference's own .gp-app-grid/.gp-
   app-icon/.gp-app-pager, translated onto this app's shared tokens rather than its own
   private literals. Paging itself (which tiles are on screen, the dots, the swipe) is
   launcher.js's job; this is only how a tile and the pager controls are drawn. */
#app-grid {
  display: grid;
  grid-template-columns: repeat(var(--launcher-columns, 4), minmax(0, 1fr));
  grid-auto-rows: var(--launcher-row-height, auto);
  /* The stage's reserved room below a partly filled page is swipe space, not taller rows. */
  align-content: start;
  gap: calc(var(--space-xl) + var(--space-2xs)) var(--space-sm);
  padding: 9px var(--page-gutter) 0;
  touch-action: pan-y;
}

/* --launcher-columns and --launcher-row-height are written onto #app-grid by launcher.js's
   own syncGrid(), the one place in this pane where JS decides the grid's shape: columns from
   the cell the labels need, the row height from the tallest tile. */

/* A grid tile is exactly the dock's tile: the same --dock-tile ground, dock label type,
   tracking and gap; only the label's two-line clamp is the grid's own. */
.app-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-sm);
  min-width: 0;
  min-height: 0;
  padding: 0;
  color: var(--ink-heading);
  background: transparent;
  border: 0;
  border-radius: 0;
  font-size: var(--text-dock-label);
  letter-spacing: var(--text-dock-label-tracking, 0);
  line-height: 1.25;
  text-align: center;
  text-shadow: var(--text-shadow-on-wall);
}

.app-tile:focus-visible {
  outline: none;
}

.app-tile:focus-visible .app-tile-icon {
  box-shadow: var(--shadow-tile-icon), 0 0 0 2px var(--icon-gold);
}

.app-tile[hidden] {
  display: none;
}

/* Pagination's own visibility, distinct from [hidden] (which means "not a destination
   yet", the Screen tile's own state): a page change hides every tile outside the current
   page without touching whether that tile is a destination at all. */
.app-tile.launcher-tile-hidden {
  display: none;
}

/* A tile that lives in the dock (launcher-editor.js): it is not a grid position. */
.app-tile.app-tile-docked {
  display: none;
}

/* A page taken off the selector (launcher-editor.js): kept in the DOM, shown nowhere. */
.app-tile.app-tile-removed {
  display: none;
}

.app-tile:active .app-tile-icon {
  transform: scale(0.93);
  box-shadow: var(--shadow-tile-icon-active);
}

/* The icon tile: a beveled square in the destination's own gradient ground, --app-ink set
   inline per tile (index.html) so one rule draws all fourteen colours; the ground carries
   an even tint of that same ink. */
.app-tile-icon {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  overflow: hidden;
  width: var(--dock-tile);
  height: var(--dock-tile);
  border-radius: var(--radius-tile);
  background: var(--icon-tile-ground-tinted);
  box-shadow: var(--shadow-tile-icon-dock);
  color: var(--app-ink, var(--ink-heading));
  transition: transform var(--motion-press) ease, box-shadow var(--motion-press) ease;
}

.app-tile-icon[hidden] {
  display: none;
}

.app-tile-icon svg {
  width: var(--dock-glyph);
  height: var(--dock-glyph);
  stroke: currentColor;
  fill: none;
  stroke-width: 1.85;
}

/* The polygon bleeds past the box so the icon's shadow and focus ring still show. */
.app-tile[data-owner="person"] .app-tile-icon,
.dock-button[data-owner="person"] .dock-icon,
.car-app[data-owner="person"] .car-app-icon {
  --dog-ear: 25%;
  --bleed: 24px;
  clip-path: polygon(calc(0px - var(--bleed)) calc(0px - var(--bleed)), calc(100% - var(--dog-ear) - var(--bleed)) calc(0px - var(--bleed)), calc(100% + var(--bleed)) calc(var(--dog-ear) + var(--bleed)), calc(100% + var(--bleed)) calc(100% + var(--bleed)), calc(0px - var(--bleed)) calc(100% + var(--bleed)));
}

.app-tile[data-owner="person"] .app-tile-icon::after,
.dock-button[data-owner="person"] .dock-icon::after,
.car-app[data-owner="person"] .car-app-icon::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: var(--dog-ear);
  aspect-ratio: 1;
  background: linear-gradient(to top right, color-mix(in srgb, var(--person-color, var(--accent)) 86%, var(--ink)) 0 50%, transparent 50%);
  pointer-events: none;
}

/* An installed Android app's tile: the same icon ground as a page tile (size, radius,
   shadow, position), with the app's own icon inset inside it like a page glyph. --app-ink
   is deliberately unset, so the ground's tint falls back to the heading ink. The letter
   shows until the icon has loaded. */
.app-tile-native-img {
  width: calc(var(--dock-tile) * var(--launcher-native-inset));
  height: calc(var(--dock-tile) * var(--launcher-native-inset));
  object-fit: contain;
  border-radius: var(--radius-nested);
}

.app-tile-native-letter {
  color: var(--ink-heading);
  font-size: var(--text-page-title);
  font-weight: 600;
  line-height: 1;
  text-transform: uppercase;
}

.app-tile-unavailable .app-tile-icon {
  opacity: 0.55;
}

/* Arranging the All selector (native-apps.js). Normal taps are untouched; the editing rules
   only apply while #apps has .apps-editing. Tiles never select text or pop the long-press
   menu. */
#app-grid .app-tile {
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* Tap-tap arranging (launcher-editor.js): select one app, then tap a destination. Selection
   is a slight scale + gold ring on the icon, never a jiggle; every other filled tile fades but stays
   readable, and empty positions stay fully visible as the places a selection can go. */
.apps-editing .app-tile {
  transition: opacity var(--motion-press) ease;
}

#apps[data-has-selection] .app-tile:not(.app-edit-selected):not(.app-tile-slot) {
  opacity: 0.55;
}

.app-tile.app-edit-selected .app-tile-icon {
  transform: scale(1.05);
  box-shadow: var(--shadow-card), 0 0 0 3px var(--icon-gold);
}

.app-tile.app-edit-selected {
  font-weight: 600;
}

.app-tile-slot .app-tile-icon {
  background: transparent;
  box-shadow: none;
  border: 2px dashed color-mix(in srgb, var(--ink-on-wall-muted) 70%, transparent);
  box-sizing: border-box;
}

#apps[data-has-selection] .app-tile-slot .app-tile-icon {
  border-color: var(--icon-gold);
}

.app-grid-stage {
  position: relative;
  touch-action: pan-y;
}

/* The grid stretches to the stage's reserved room so a pointerdown on the empty cells of a
   partly filled last page still lands inside the stage's swipe subtree. */
.app-grid-stage > #app-grid {
  box-sizing: border-box;
  min-height: 100%;
}

.app-edit-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--touch-min);
  height: 64px;
  padding: 0;
  color: var(--ink-heading);
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  border: 0;
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-tile-icon-dock);
  transition: opacity var(--motion-press) ease;
}

.app-edit-arrow-prev {
  left: max(2px, var(--safe-left));
}

.app-edit-arrow-next {
  right: max(2px, var(--safe-right));
}

.app-edit-arrow[hidden] {
  display: none;
}

.app-edit-arrow:disabled {
  opacity: 0.3;
  cursor: default;
}

.app-edit-arrow svg {
  width: 28px;
  height: 28px;
  stroke-width: 2;
}

/* The arrows sit beside the grid, never over an icon. Only below the wide breakpoint, where
   the grid spans the viewport; above it the grid's centred max-width leaves side room. */
@media (min-width: 375px) and (max-width: 47.99rem) {
  :root:not([data-text-size="large"]) .apps-editing .app-grid-stage:has(> .app-edit-arrow:not([hidden])) #app-grid {
    padding-inline: calc(max(2px, var(--safe-left)) + 46px) calc(max(2px, var(--safe-right)) + 46px);
  }
}

/* On a narrow phone or with Large text the side arrows would steal grid width, so the same
   two buttons sit beneath the grid instead. */
@media (max-width: 374px) {
  .apps-editing .app-grid-stage {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-xl);
  }

  .apps-editing #app-grid {
    flex: 1 0 100%;
    order: 0;
  }

  .apps-editing .app-edit-arrow {
    position: static;
    transform: none;
    width: 64px;
    height: 48px;
    order: 1;
  }
}

@media (max-width: 47.99rem) {
  :root[data-text-size="large"] .apps-editing .app-grid-stage {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-xl);
  }

  :root[data-text-size="large"] .apps-editing #app-grid {
    flex: 1 0 100%;
    order: 0;
  }

  :root[data-text-size="large"] .apps-editing .app-edit-arrow {
    position: static;
    transform: none;
    width: 64px;
    height: 48px;
    order: 1;
  }
}

/* The pager's own small arrows duplicate the big ones while editing; hidden, not removed,
   so the dots stay centred. */
.apps-editing .app-pager button:not(.app-pager-dot) {
  visibility: hidden;
}

/* The edit bar sits at the bottom of the pane (sticky inside the scroller, never fixed over
   the dock). Compact at every text size: the hint, the optional Remove button and the reset
   confirm each take a row of their own, while Reset layout, Cancel and Done share one (zero
   flex-basis, so they shrink to fit rather than wrapping). */
.app-edit-bar {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-top: auto;
  padding: 0.375rem var(--space-r2);
  background: var(--panel);
  border-top: 1px solid var(--line-strong);
}

.app-edit-bar[hidden],
.app-edit-bar button[hidden] {
  display: none;
}

.apps-editing {
  padding-bottom: var(--app-edit-bar-space, 0);
}

/* The composer has no role while arranging: its room goes to the grid. The dock stays, its
   Favorites slots are edit targets. launcher-editor.js sets the class on <body> for exactly
   the length of an edit, so the composer returns whole on Done, Cancel or an interruption. */
body.launcher-editing .composer-row {
  display: none;
}

.app-edit-hint {
  flex: 1 0 100%;
  margin: 0;
  color: var(--ink-heading);
  font-size: var(--text-detail-meta);
  font-weight: 600;
}

.app-edit-keep {
  display: flex;
  flex: 1 0 100%;
  gap: var(--space-r2);
  align-items: center;
  font-size: var(--text-detail-meta);
}

.app-edit-keep[hidden] {
  display: none;
}

.app-edit-keep-hint {
  color: var(--muted);
}

.app-edit-bar button {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--touch-min);
  padding-inline: 0.375rem;
  color: var(--ink);
  background: var(--panel-subtle);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--text-control);
  line-height: 1.2;
}

/* Only shown with a selection: its own row, so it never squeezes the three above. */
#app-remove-shortcut {
  flex-basis: 100%;
}

#app-edit-done,
#app-edit-reset-yes {
  color: var(--on-accent);
  background: var(--accent);
  border-color: transparent;
  font-weight: var(--w-title);
}

#app-edit-done:hover,
#app-edit-done:focus-visible,
#app-edit-reset-yes:hover,
#app-edit-reset-yes:focus-visible {
  background: var(--accent-hover);
}

.app-edit-reset-confirm {
  display: flex;
  flex: 1 0 100%;
  flex-wrap: wrap;
  gap: var(--space-r2);
}

.app-edit-reset-confirm[hidden] {
  display: none;
}

.app-edit-reset-confirm p {
  flex: 1 0 100%;
  margin: 0;
  color: var(--ink);
  font-weight: 400;
}

/* The dock while arranging: a Favorites heading and a visible frame per slot. Outside
   editing the dock is exactly as compact as before. */
#dock-nav[data-editing]::before {
  content: 'Favorites';
  grid-column: 1 / -1;
  padding-bottom: var(--space-xs);
  color: var(--ink-on-wall-muted);
  font-size: var(--text-caption);
  letter-spacing: 0.04em;
  text-align: center;
  text-shadow: var(--text-shadow-on-wall);
}

#dock-nav[data-editing] .dock-button {
  transition: opacity var(--motion-press) ease;
}

#dock-nav[data-editing] .dock-icon {
  transition: transform var(--motion-press) ease, box-shadow var(--motion-press) ease;
}

#dock-nav[data-editing] .dock-button[data-dock-slot] {
  background: color-mix(in srgb, var(--panel) 30%, transparent);
  border-radius: var(--radius-control);
  outline: 1.5px solid color-mix(in srgb, var(--line-strong) 80%, transparent);
  outline-offset: -2px;
}

#dock-nav[data-editing] #dock-all {
  opacity: 0.45;
}

#dock-nav[data-has-selection] .dock-button[data-dock-slot]:not(.app-edit-selected):not([data-dock-empty]) {
  opacity: 0.6;
}

#dock-nav[data-editing] .dock-button.app-edit-selected {
  font-weight: 600;
}

#dock-nav[data-editing] .dock-button.app-edit-selected .dock-icon {
  transform: scale(1.05);
  box-shadow: var(--shadow-tile-icon-dock), 0 0 0 2.5px var(--icon-gold);
}

.dock-button[data-dock-empty] .dock-icon {
  background: transparent;
  box-shadow: none;
  border: 1.5px dashed color-mix(in srgb, var(--ink-on-wall-muted) 70%, transparent);
}

#dock-nav[data-has-selection] .dock-button[data-dock-empty] .dock-icon {
  border-color: var(--icon-gold);
}

.dock-button[data-dock-empty] .dock-label::before {
  content: 'Empty';
}

@media (prefers-reduced-motion: reduce) {
  .apps-editing .app-tile,
  .app-edit-arrow,
  #dock-nav[data-editing] .dock-button,
  #dock-nav[data-editing] .dock-icon,
  .app-tile.app-edit-selected .app-tile-icon {
    transition: none;
  }
}

/* Rendered on every native tile but empty (display: none) unless the app is missing. The
   "Not installed" text hangs absolutely just under the label, outside the tile's box, so no
   tile is ever taller than its siblings and nothing moves under a finger. */
.app-tile-sub {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: calc(-1 * var(--space-xs));
  color: var(--muted);
  font-size: var(--text-caption);
  line-height: 1.3;
  text-align: center;
  pointer-events: none;
}

.app-tile-sub:empty {
  display: none;
}

/* The All page's actions join the title row; the shared top-button primitive supplies their
   wallpaper treatment and touch sizing. */
.app-launcher-actions {
  display: contents;
}

.app-launcher-actions[hidden] {
  display: none;
}

.add-app-dialog[open][data-viewport-shrunk] {
  position: fixed;
  inset-block: auto;
  top: var(--add-app-top);
  margin-block: max(0.5rem, var(--safe-top)) 0;
  max-height: calc(var(--add-app-height) - max(0.5rem, var(--safe-top)) - 0.5rem);
}

.add-app-dialog[open][data-viewport-shrunk] .app-dialog-frame,
.add-app-dialog[data-viewport-shrunk] .app-dialog-frame {
  max-height: calc(var(--add-app-height) - max(0.5rem, var(--safe-top)) - 0.5rem);
}

.add-app-dialog[data-viewport-shrunk] .add-app-list {
  max-height: none;
  overflow: visible;
}

.add-app-dialog[data-viewport-shrunk] .add-app-body {
  padding-bottom: calc(0.9rem + var(--safe-bottom, 0px));
}

.add-app-search-label {
  display: block;
  margin-bottom: 0.3rem;
  color: var(--muted);
}

.add-app-search {
  box-sizing: border-box;
  min-height: var(--touch-min);
}

.add-app-search-row {
  margin-bottom: 0.6rem;
}

.add-app-list {
  max-height: min(45vh, 24rem);
  max-height: min(45dvh, 24rem);
  margin: 0;
  padding: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
}

.add-app-list > li[hidden] {
  display: none;
}

#add-app-empty {
  margin-top: 0.6rem;
}

.add-app-footer {
  flex-wrap: wrap;
  align-items: center;
}

.add-app-footer #add-app-get-more {
  margin-right: auto;
  background: var(--panel-subtle);
  color: var(--ink);
  border-color: var(--line-strong);
}

.add-app-error {
  flex: 1 0 100%;
  order: -1;
  margin: 0;
}

.add-app-row {
  display: flex;
  align-items: center;
  gap: var(--space-r3);
  width: 100%;
  min-height: 56px;
  padding: 0.4rem var(--space-r2);
  background: transparent;
  color: var(--ink);
  border: 0;
  border-radius: var(--card-radius);
  text-align: left;
}

.add-app-row-icon {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-tile);
  background: var(--icon-tile-ground);
  box-shadow: var(--shadow-tile-icon-dock);
  overflow: hidden;
}

/* A Rundell page's row: its tile's own svg, drawn in the tile's --app-ink. */
.add-app-row-page .add-app-row-icon {
  color: var(--app-ink, var(--ink-heading));
}

.add-app-row-icon svg {
  width: 20px;
  height: 20px;
  stroke-width: 1.85;
}

.add-app-row-img {
  width: calc(40px * var(--launcher-native-inset));
  height: calc(40px * var(--launcher-native-inset));
  object-fit: contain;
  border-radius: var(--radius-nested);
}

.add-app-row-letter {
  color: var(--ink-heading);
  font-weight: 600;
  line-height: 1;
  text-transform: uppercase;
}

.add-app-row-label {
  display: -webkit-box;
  flex: 1 1 auto;
  min-width: 0;
  line-height: 1.3;
  overflow-wrap: break-word;
}

.add-app-row-state {
  flex: none;
  color: var(--muted);
}

.add-app-row-added {
  cursor: default;
  opacity: 0.75;
}

/* Full column width (wider than the icon above it), wrapping between words
   onto up to two lines with line-clamp 2 and normal word breaks. Every tile
   reserves two label lines so rows have one height whether or not a label
   wraps. */
.app-tile-label {
  align-self: stretch;
  min-height: 2.5em;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  white-space: normal;
  overflow-wrap: normal;
  word-break: normal;
}

.app-tile-label[hidden] {
  display: none;
}

/* The full label stays in aria-label/title even when the shared two-line tile rule is reached. */

@media (prefers-reduced-motion: reduce) {
  .app-tile:active .app-tile-icon {
    transform: none;
  }
}

/* The pager: Previous, a dot per page and Next; the "N of M" status is a visually hidden
   live region. Hidden outright with one page (launcher.js sets [hidden] on both) -- there
   is nothing to page between, so nothing here should announce or occupy space. */
.app-pager {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-xl);
  margin: 9px 22px 3px;
}

.app-pager[hidden] {
  display: none;
}

.app-pager button {
  display: grid;
  place-items: center;
  width: var(--touch-min);
  height: var(--touch-min);
  padding: 0;
  color: var(--ink-on-wall-muted);
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.app-pager button:disabled {
  opacity: 0.25;
  cursor: default;
}

.app-pager button svg {
  width: 20px;
  height: 20px;
}

.app-pager-dots {
  display: flex;
  gap: 0;
  min-width: 0;
}

.app-pager-dots[hidden] {
  display: none;
}

.app-pager-dot {
  display: grid;
  place-items: center;
  flex: 0 0 var(--touch-min);
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  background: transparent;
  border: 0;
}

.app-pager-dot[hidden] {
  display: none;
}

.app-pager-dot::after {
  content: '';
  width: 6px;
  height: 6px;
  background: color-mix(in srgb, var(--ink-on-wall-muted) 60%, transparent);
  border-radius: var(--radius-pill);
  transition: width var(--motion-press) ease, background var(--motion-press) ease;
}

.app-pager-dot[aria-pressed="true"]::after {
  width: 20px;
  background: var(--accent);
}

/* The page-change slide, launcher.js's own animate() -- a plain CSS animation the same
   way app-page-enter above is, restarted each time the class is removed and re-added
   rather than relying on a display change to restart it (the grid never actually leaves
   the render tree between pages). --launcher-slide-from carries the direction: +22px
   arriving from the right (Next), -22px from the left (Previous). */
@keyframes launcher-slide {
  from {
    transform: translateX(var(--launcher-slide-from, 22px));
    opacity: 0.55;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.launcher-slide {
  animation: launcher-slide var(--motion-page) var(--ease-page);
}

@media (prefers-reduced-motion: reduce) {
  .launcher-slide {
    animation: none;
  }
}

/* Tablet/desktop: the grid is only centred with a max-width here; its columns come from
   launcher.js's measured fit. */
@media (min-width: 48rem) {
  #app-grid {
    max-width: 60rem;
    margin-inline: auto;
    width: 100%;
    box-sizing: border-box;
  }

  #apps .app-page-header {
    width: 100%;
    max-width: 60rem;
    margin-inline: auto;
    box-sizing: border-box;
  }

}

/* A short phone screen (320x640, 360x640): only the header padding and the grid gap give
   way. The icons and labels never shrink with the viewport's height; launcher.js fits
   fewer rows instead, and the pane scrolls when even one row will not fit above the
   command bar. */
@media (max-height: 700px) {
  #apps .app-page-header {
    padding-top: var(--space-lg);
    padding-bottom: var(--space-xl);
  }

  #app-grid {
    gap: var(--space-lg) var(--space-sm);
  }

}
