/* Homepage — lists every saved presentation.
   Owned by src/home.js.
   Loaded from home.html only; no other page needs it. See the ownership
   lanes in CLAUDE.md. */

.home {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

/* Fills the shared bar's centre slot (app-bar.js), which already holds it
   to a search field's width. */
.home__search { position: relative; min-width: 160px; }
.home__search-icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-soft);
  pointer-events: none; /* the icon sits over the input, not in front of it */
}
.home__search-input {
  width: 100%;
  height: 32px;
  padding: 0 12px 0 34px; /* left padding clears the icon */
  font: inherit;
  color: var(--ink);
  background: var(--bg-panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
}
.home__search-input::placeholder { color: var(--ink-soft); }
.home__search-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* A plain link to another page — neither filled nor bordered, so the
   accent-filled "New presentation" below the bar stays the one thing on the
   page that reads as "the" action; the hover fill is what says these can be
   pressed. Shared by Asset library, Manage users and Backup, each role-gated
   independently in home.js. */
.home__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px 0 12px;
  font: inherit;
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  cursor: var(--cursor-pointer);
  text-decoration: none;
}
.home__link .icon { color: var(--ink-soft); }
.home__link:hover { background: var(--bg-hover); }
.home__link:hover .icon { color: var(--ink); }
.home__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Backup is reached for rarely, so it keeps only its glyph — square, the
   same height as its labelled neighbours. */
.home__link--icon { width: 32px; padding: 0; justify-content: center; }

.home__new {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px 0 12px;
  font: inherit;
  font-weight: 500;
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: var(--radius);
  cursor: var(--cursor-pointer);
}
.home__new .icon { color: #fff; }
.home__new:hover { filter: brightness(1.06); }
.home__new:active { filter: brightness(0.96); }
.home__new:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The signed-in person's own picture — display only: the photo comes from
   the Google Workspace directory, not an upload here. Round rather than the
   rest of the bar's rounded-rectangle shape, the same reading users.css's own
   .usersavatar and comments.css's own .commentavatar already give a
   person's photo everywhere else in this app. */
.home__avatar {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  overflow: hidden;
}
.home__avatar-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.home__avatar-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  background: var(--accent);
}

/* An admin's avatar is a button — "View as" (home.js). A ring says the
   picture is someone else's while that view is on, and the admin's own
   sits small over its bottom-right corner. The clip moves to the inner
   picture so that badge can hang past the circle. */
.home__avatar--button {
  position: relative;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: visible;
  cursor: var(--cursor-pointer);
}
.home__avatar-main {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
}
.home__avatar-badge {
  position: absolute;
  right: -6px;
  bottom: -4px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  overflow: hidden;
  /* The panel colour as a cut-out, so the two pictures read as stacked
     rather than merged. */
  box-shadow: 0 0 0 2px var(--bg-panel);
}
.home__avatar-badge .home__avatar-initials { font-size: 8px; }
.home__avatar--button:hover { box-shadow: 0 0 0 3px var(--bg-hover); }
.home__avatar--button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.home__avatar--button.is-viewing-as { box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 4px var(--accent); }

/* Full width under the bar, not inside the section: it describes the whole
   page, so it pins to the top once the bar scrolls away. Its height is
   fixed and published as --home-pinned, which the list view's own pinned
   group headings sit below instead of sliding under it. */
.home { --home-pinned: 0px; }
.home.is-viewing-as { --home-pinned: 48px; }
.home__viewas {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 48px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 32px;
  background: var(--bg-accent-soft);
  color: var(--ink);
  font-size: 13px;
}
.home__viewas[hidden] { display: none; }
.home__viewas .icon { flex: none; color: var(--accent); }
.home__viewas-text { flex: 1; min-width: 0; }
.home__viewas-exit {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-weight: 500;
  cursor: var(--cursor-pointer);
}
.home__viewas-exit:hover { filter: brightness(1.06); }

.home__body {
  flex: 1;
  padding: 32px 32px 48px;
  /* Room enough for the grid to breathe without ever feeling tethered to the
     edge of a very wide window — the cards are the thing to look at, the
     same reason the toolbar hugs its tools instead of spanning the canvas. */
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
}

/* One of what will eventually be several — "Recent presentations" is the
   only one today, but the heading is what makes a second (shared decks,
   templates, whatever comes next) read as its own section rather than a
   second grid nobody labelled. */
.home__section + .home__section { margin-top: 40px; }

/* The title and the list's own controls — the card/list toggle and "New
   presentation" — share one row, so they sit at the title's own baseline
   rather than floating separately above the grid. "New presentation" lives
   here rather than in the header because it adds to this list; the header
   is for getting somewhere else.
   The bottom margin that used to sit on the title itself moved here, since
   the title's own margin has to be 0 once something else is beside it. */
.home__section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
.home__section-actions { display: flex; align-items: center; gap: 10px; }
.home__section-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* Same shape as .deckcard__menu below — a plain icon button, transparent
   until hovered — since both are "an icon button that isn't the primary
   action of its row". Shows the view a click switches *to*, not the one
   already showing (see updateViewToggle in home.js), so there is only ever
   one icon to draw rather than a highlighted pair. */
.home__view-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink-soft);
  cursor: var(--cursor-pointer);
}
.home__view-toggle:hover { background: var(--bg-hover); color: var(--ink); }
.home__view-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The owner filter beside it — the same quiet, transparent-until-hovered
   button, just carrying its current choice as a label so the grid below
   says which slice of the list it is. */
.home__filter {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  height: 28px;
  padding: 0 6px 0 10px;
  font: inherit;
  font-weight: 500;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  border-radius: var(--radius);
  cursor: var(--cursor-pointer);
  --icon-size: 14px;
}
.home__filter:hover { background: var(--bg-hover); color: var(--ink); }
/* "All presentations" is an admin looking past their own list; the accent
   keeps it from reading as the everyday view it is not. */
.home__filter.is-all,
.home__filter.is-all:hover { color: var(--accent); }
.home__filter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Fixed-width columns, not `minmax(256px, 1fr)`: a stretchy column would
   render wider than home.js's own CARD_WIDTH on a wide window, and that
   constant is what the scale math sizing each card's cover is built from —
   drifting apart would misalign the cover inside its own frame. A fixed
   width leaves leftover row space as trailing gap instead of stretching to
   fill it, the same trade `auto-fill` (rather than `auto-fit`) already makes. */
.deckgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 256px);
  gap: 28px 24px;
}
/* List format: full-width rows grouped under time-based headings (Today,
   Yesterday, …), rather than a grid of covers — see the toggle in
   .home__section-head. A plain column of groups, not a grid, since a row's
   own width is whatever the section gives it rather than a fixed card
   width. The gap between groups is the wider one; the tighter gap between
   rows sharing a heading lives on .decklist-group itself. */
.deckgrid--list {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.decklist-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Shared with .deckcard--list below so the two always line up: a
   fixed-width thumbnail and menu bookend, the time label stretching to take
   up whatever room the two fixed extra columns don't. Every group's own
   heading row carries Owner / Last opened by me this way, rather than a
   single header above the whole list — each one already reads as its own
   sub-table start, the same reason "Today" and "Yesterday" get separate
   headings instead of one shared label. */
.decklist-group__head,
.deckcard--list {
  /* minmax(0, …) rather than a bare 1fr, whose floor is the name's own
     min-content: a long unbroken name widened a row's column past its
     heading's, shoving Owner and Last opened off their labels. */
  grid-template-columns: 72px minmax(0, 1fr) 140px 150px 28px;
  gap: 32px;
  align-items: center;
}
/* Pinned while its own group scrolls past — plain `position: sticky; top: 0`
   is the whole trick: every group's own heading sits in the same normal
   flow, un-stickied, until scrolling would carry it past the viewport's own
   top edge, at which point it holds there instead. The *next* group's own
   heading, stickied the same way, naturally takes over the instant its own
   flow position reaches that same top edge — nothing here has to track
   which group is "current" or swap one heading's content for another's, the
   browser already stacks later siblings over earlier ones in document
   order. An opaque background is what makes that handoff read as one
   heading replacing another rather than two text layers overlapping while
   the rows in between scroll past underneath. */
.decklist-group__head {
  display: grid;
  /* Taller than a plain row on purpose — pinned, it reads as a bar the rows
     scroll past underneath, not just another row that happens to hold
     still, so it gets more presence than the 6px/8px an ordinary row gets.
     align-items: center (shared rule above) is what actually centres the
     text within that extra height, not anything here. */
  padding: 14px 8px;
  position: sticky;
  top: var(--home-pinned);
  /* Above every row: a row is `.deckcard`, which is itself `position:
     relative` with no z-index of its own (so it stacks at the auto/0 level
     among its siblings) — 1 would already clear that, but naming a value
     with headroom past it is what actually reads as "above the list" rather
     than "one more than whatever 0 happens to mean". */
  z-index: 5;
  background: var(--bg-chrome);
  border-bottom: 1px solid var(--line);
}
/* Spans the thumbnail+name columns — there is no label over either one (a
   presentation's own name needs no column header to read), so the time
   heading takes their place instead of sitting only over its own. */
.decklist-group__title {
  grid-column: 1 / 3;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
}
.home__column-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
}

.home__empty {
  margin-top: 64px;
  text-align: center;
  color: var(--ink-soft);
}

/* The sign-in-failed state (home.js's renderSignInStuck). Centred on the
   body the way .home__empty is, but louder: that one is an invitation to
   start a deck, this one is the page saying it could not establish who you
   are and offering the one action that fixes it. */
.home__stuck {
  margin: 96px auto 0;
  max-width: 420px;
  text-align: center;
  color: var(--ink);
}

.home__stuck-icon {
  --icon-size: 32px;
  color: var(--ink-soft);
}

.home__stuck-title {
  margin: 16px 0 8px;
  font-size: 18px;
  font-weight: 600;
}

.home__stuck-text {
  margin: 0 0 24px;
  color: var(--ink-soft);
  line-height: 1.5;
}

/* position: relative anchors two things against the card as a whole:
   .deckcard__link below (the full-card click target) and .deckcard__menu's
   own row further down. */
.deckcard { position: relative; display: flex; flex-direction: column; gap: 10px; }

/* The whole card's own click target, full native <a> behaviour included —
   middle-click, cmd/ctrl-click, right-click's own "open in new tab" — which
   a plain click handler on a <div> would have to reimplement by hand. It
   sits *under* the visual layers instead of wrapping them, because a button
   cannot be a descendant of an <a> without the two disagreeing about what a
   click on it means; the frame, the text and the menu button below are all
   painted on top of this one and read straight through to it wherever they
   carry `pointer-events: none`, so most of a card still opens the deck. */
.deckcard__link { position: absolute; inset: 0; z-index: 0; cursor: var(--cursor-pointer); }
.deckcard__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-thumb); }

.deckcard__frame {
  position: relative;
  z-index: 1;
  pointer-events: none; /* deckcard__link underneath is the real click target */
  overflow: hidden;
  /* Every cover the same shape as a 1920x1080 slide, whatever the deck
     underneath it was actually authored at — see CARD_HEIGHT in home.js. */
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-thumb);
  background: #fff;
  box-shadow: 0 1px 2px rgba(16, 20, 26, 0.10), 0 4px 12px rgba(16, 20, 26, 0.08);
  transition: box-shadow 0.12s ease, transform 0.12s ease;
}
.deckcard:hover .deckcard__frame {
  box-shadow: 0 2px 4px rgba(16, 20, 26, 0.12), 0 8px 20px rgba(16, 20, 26, 0.12);
  transform: translateY(-1px);
}
/* Same reason the slide list's own thumbnail frame carries this ring instead
   of an inset shadow on the scaled canvas itself: it paints over the content
   rather than under it. */
.deckcard__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(16, 20, 26, 0.10);
}
/* Absolute, not relative: home.js centres a deck that doesn't match the
   frame's own 16:9 by setting left/top itself (see buildCard), which needs
   this positioned against the frame rather than sitting in flow. */
.deckcard__canvas { position: absolute; transform-origin: 0 0; pointer-events: none; }

/* A row, not a column, so the menu button sits to the text's own right —
   align-items: center is what "vertically aligned to the text" actually
   means here, against however tall the name/time pair turns out to be. */
.deckcard__meta {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 2px;
  pointer-events: none; /* deckcard__link underneath; .deckcard__menu opts back in */
}
.deckcard__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* Wraps .deckcard__name rather than the other way round, so startRename's
   own replaceWith(input) can swap just the name span and leave the icon (see
   isShared, home.js) sitting exactly where it was either side of that swap.
   min-width: 0 on both — a flex child's own default min-width is its content
   size, which would stop the name from ever actually ellipsising. */
.deckcard__name-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.deckcard__name {
  min-width: 0;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A deck someone else can now reach, not a control — see share.js's own
   modal for the one place that's actually decided. Sized down from the
   16px .icon default the same reason a swatch's own caret already is,
   since it sits inline with a line of text rather than inside a button. */
.deckcard__shared-icon {
  flex: none;
  display: inline-flex;
  color: var(--ink-soft);
}
.deckcard__shared-icon .icon { --icon-size: 13px; }
/* Renaming in place (see startRename in home.js) swaps .deckcard__name for
   this rather than a separate dialog — the same "type over the label"
   pattern the open editor's own .deckname field (slides.js) uses. The
   negative margin offsets its own border/padding so the row doesn't reflow
   the moment editing starts. */
.deckcard__name-input {
  font: inherit;
  font-weight: 500;
  color: var(--ink);
  background: var(--bg-panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: 2px 4px;
  margin: -3px -5px;
  width: 100%;
  pointer-events: auto;
}
.deckcard__name-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.deckcard__time { font-size: 12px; color: var(--ink-soft); }

/* The one control in a card that isn't the link: re-opts into pointer-events
   so its own click never falls through to .deckcard__link beneath it. */
.deckcard__menu {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink-soft);
  cursor: var(--cursor-pointer);
  pointer-events: auto;
}
.deckcard__menu:hover { background: var(--bg-hover); color: var(--ink); }
.deckcard__menu:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A list row is the same .deckcard — same link/menu/rename behaviour, just
   laid out as a grid (columns shared with .decklist-group__head above)
   instead of stacked with a full-width cover. Its own five children (frame,
   text, owner, last-opened, menu) sit straight in those columns — there is
   no .deckcard__meta wrapper here the way the card layout has one, since a
   grid needs each column filled by a direct child, not a flex pair nested
   inside one of them. */
.deckcard--list {
  display: grid;
  /* Restated: .deckcard's own 10px gap sits later in this sheet than the
     shared column rule above and would otherwise win for rows alone,
     leaving them out of line with their heading. */
  gap: 32px;
  padding: 6px 8px;
  border-radius: var(--radius);
}
.deckcard--list:hover { background: var(--bg-hover); }
/* A hairline between rows, not under the group's own heading — an adjacent-
   sibling selector rather than :not(:last-child) skips that pairing for
   free, since a row's only sibling above the first is the heading, never
   another row. */
.deckcard--list + .deckcard--list { border-top: 1px solid var(--line); }
/* A cover this small reads as over-rounded at the card's own 16px
   (--radius-thumb, tuned for the 256px card) — the plain control radius
   scales down with it instead. */
.deckcard--list .deckcard__frame { border-radius: var(--radius); }
/* The lift-on-hover the card frame gets reads as clutter next to a plain
   row — the row's own background change above is feedback enough. */
.deckcard--list:hover .deckcard__frame { transform: none; }
.deckcard__owner,
.deckcard__last-opened {
  font-size: 13px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The owner column is a name with a face, not just a name — .deckcard__owner
   itself stays the text rule above (colour, size, its own ellipsis), this
   just turns it into a row so the avatar and the name/"You" beside it don't
   have to be a single overflowing string. */
.deckcard__owner { display: flex; align-items: center; gap: 6px; }
.deckcard__owner-avatar {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  overflow: hidden;
  font-size: 9px;
  font-weight: 600;
  color: #fff;
}
/* avatarImg (comments.js) hands back a .commentavatar, whose sizing lives in
   comments.css — a sheet this page does not load — so the photo came in at
   its natural size and the initials as a bare inline span. Filling the
   circle here is cheaper than pulling in the whole comments sheet. */
.deckcard__owner-avatar .commentavatar {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  line-height: 1;
}
.deckcard__owner-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------- delete dialog */
/* Stands in for the native confirm() a card's own "Delete" used to raise —
   that dialog draws in the OS's own chrome, not this app's, so a saved dark
   preference (or the light theme) had no way to reach it. The same plain
   backdrop + centred box slides.js's own copydialog is, and for the same
   reason: small and self-contained beats a cross-lane import for one popup. */
.deleteconfirm {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(16, 20, 26, 0.32);
}
.deleteconfirm__box {
  width: 320px;
  padding: 20px;
  background: var(--bg-panel);
  border-radius: var(--radius-thumb);
  box-shadow: 0 8px 24px rgba(16, 20, 26, 0.20);
}
.deleteconfirm__title {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.deleteconfirm__body {
  margin: 0 0 16px;
  color: var(--ink-soft);
  line-height: 1.4;
}
.deleteconfirm__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.deleteconfirm__cancel,
.deleteconfirm__delete {
  height: 32px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radius);
  font: inherit;
  cursor: var(--cursor-pointer);
}
/* The non-destructive choice is the filled one — Cancel is what dismissing
   the dialog any other way (Escape, a click on the backdrop) already does,
   so it reads as the default, and Delete as the deliberate departure from
   it — the same weighting slides.js's own copydialog gives Keep/Remove. */
.deleteconfirm__delete { background: transparent; color: var(--danger); }
.deleteconfirm__delete:hover { background: var(--bg-danger-soft); }
.deleteconfirm__cancel { background: var(--accent); color: #fff; font-weight: 500; }
.deleteconfirm__cancel:hover { filter: brightness(1.06); }
