/* shell.css — SITE adaptation of ui-components/checkbox-svg/style.css.
   Deltas from the component (which styles <html>/<body> directly):
   - .shell replaces body as the layout root: a fixed overlay (inset: 0
     supplies the height the component gets from body's 100dvh), so the
     landing stage in <main> keeps owning the document until Tap to Begin;
     its display is gated in site/index.html (none until body.app)
   - the component's html scrollbar styling moves onto .shell
     (scrollbar-width/-color inherit, so descendants pick it up)
   Keep in sync with the component when it changes. */

.shell {
  position: fixed;
  inset: 0;
  background-color: black;
  /* priority grid: the horizontal bars own the FULL window width; the
     left/right rails live between them. minmax(0, 1fr) so the center track
     can shrink below its content and scroll internally instead of growing
     the page. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "top    top    top"
    "left   center right"
    "bottom bottom bottom";
  scrollbar-width: thin;
  scrollbar-color: white transparent;
}

.svg-defs {
  display: none;
}

.left-sidebar,
.right-sidebar {
  display: flex;
  flex-direction: column;
}

.left-sidebar {
  grid-area: left;
  align-items: flex-start;
}

.right-sidebar {
  grid-area: right;
  align-items: flex-end;
}

/* dissolved: its children (top-bar, center, bottom-bar) place themselves on
   the root grid, which is what lets the bottom bar span the rails' columns */
.center-bars {
  display: contents;
}

.center {
  grid-area: center;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  min-height: 0;
  overflow-y: auto;
}

.iframe-container {
  display: flex;
  flex-direction: column;
  min-width: 30rem;
  min-height: 3rem;
  resize: both;
  overflow: hidden;
  border: 1px dotted white;
  border-radius: 5px;
}

.iframe-term {
  /* stacked child minimums: 1.5rem top strip + 0.5rem bottom strip
     + 2px strip borders + 50px iframe floor */
  min-height: calc(2rem + 52px);
  max-height: 300px;
  max-width: calc(82rem + 2px);
}

.iframe-term iframe {
  min-height: 50px;
}

.iframe-term .iframe-nav p::before {
  /* spawned panes carry their random id in --pane-id (attr() can't read
     ancestor attributes, so the id is mirrored into an inherited variable),
     and the flavor they were launched with in --pane-flavor (unset = default) */
  content: "Terminal: " var(--pane-id, "") var(--pane-flavor, "");
}

/* The conversation pane names what it is holding, the way a terminal pane
   names its session. The stored document's id is mirrored into --pane-id by
   the anchor click; unset until one is picked. */
.iframe-conv .iframe-nav p::before {
  content: "Conversation" var(--pane-id, "");
}

/* the active pane is the default target for injected (voice) commands:
   its dotted frame turns solid */
.iframe-term.term-active {
  border-style: solid;
}

/* dictation arming: the mic in the pane chrome is the control AND the
   indicator — dim when idle, lit when this pane receives transcribed speech */
.dictate-checkbox {
  display: none;
}

.dictate-label {
  cursor: pointer;
  padding-right: 5px;
}

.dictate-label svg {
  opacity: 0.35;
  transition: opacity 150ms ease, color 150ms ease, scale 150ms ease;
}

.dictate-label:hover svg {
  opacity: 1;
  scale: 1.1;
}

.dictate-checkbox:checked ~ svg {
  opacity: 1;
  color: #6ee7a8;
}

.iframe-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 1.5rem;
  max-height: 1.5rem;
  color: white;
  background: rgba(255, 255, 255, 0.06);
  border-bottom: 1px dotted white;
}

.iframe-ctl,
.iframe-view {
  display: flex;
}

.iframe-view {
  padding-right: 5px;
}

.iframe-close {
  --close-bg: rgba(255, 255, 255, 0.06);
  background-color: var(--close-bg);
  cursor: pointer;
  transition: transform 150ms ease, background-color 150ms ease;
}

.iframe-close:hover {
  /* relative color: redden the base tint (keep r, drop g/b, raise alpha) */
  background-color: rgb(from var(--close-bg) r calc(g - 140) calc(b - 140) / calc(alpha + 0.3));
  transform: scale(1.1);
}

.iframe-min {
  --min-bg: rgba(255, 255, 255, 0.06);
  background-color: var(--min-bg);
  cursor: pointer;
  transition: background-color 150ms ease;
}

.iframe-min:hover {
  /* relative color: yellow the base tint (keep r/g, drop b, raise alpha) */
  background-color: rgb(from var(--min-bg) r g calc(b - 140) / calc(alpha + 0.3));
}

.iframe-container p {
  margin: 0;
  font-size: 1rem;
  line-height: 1;
}

.iframe-container svg {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
}

.fullscreen-label {
  cursor: pointer;
}

.fullscreen-end {
  display: none;
}

.fullscreen-checkbox:checked ~ .fullscreen-label .fullscreen-expand {
  display: none;
}

.fullscreen-checkbox:checked ~ .fullscreen-label .fullscreen-end {
  display: inline;
}

.center-frame {
  flex: 1;
  width: 100%;
  min-width: 0;
  min-height: 0;
  border: none;
  display: block;
}

.iframe-bottom {
  display: grid;
  grid-auto-flow: column;
  min-height: 0.5rem;
  max-height: 0.5rem;
  background: rgba(255, 255, 255, 0.06);
  border-top: 1px dotted white;
}

.top-bar,
.bottom-bar {
  display: flex;
  min-width: 100%;
}

.top-bar {
  grid-area: top;
  align-items: flex-start;
}

.bottom-bar {
  grid-area: bottom;
  align-items: flex-end;
}

/* The toggle label rides the vertical middle of the STRAND BAND, not of the
   container: once the lower section opens, the container is several hundred px
   tall and centring in it drops the arrow far below the canvas it belongs to.
   Aligning to the top pins it to the band, and voice.css gives the label the
   band's own height so its (centred) glyph lands on the canvas mid-line in
   both bar states. */
.bottom-bar .sidebar-container {
  align-items: flex-start;
}

/* arrow-only toggle: the bare arrow <use> has no --arrow-display gate, so it
   shows in both states; the wrapper's rotate(90deg) below points it DOWN
   (collapse) while open, and this flip points it UP (expand) while closed */
.bottom-bar .sidebar-checkbox:not(:checked) ~ .sidebar-container .sidebar-label {
  --arrow-flip: scaleX(-1);
}

/* one composite icon, oriented per side by rotation (right rail is the 0° reference) */
.left-sidebar .sidebar-label svg {
  transform: rotate(180deg);
}

.top-bar .sidebar-label svg {
  transform: rotate(-90deg);
}

.bottom-bar .sidebar-label svg {
  /* The bare arrow's INK is not centred in its own viewBox: arrow.svg draws
     the chevron at x 8..11 of 24 — 2.5 units left of centre — because in the
     #panel-toggle composite it has to sit inside panel-frame's left pane
     (the rect is x 3..21, divided at 15). Every other toggle uses that
     composite, where frame and arrow rotate together and the offset is the
     point. This one uses the arrow ALONE, so a quarter turn stands that
     horizontal offset up into a vertical one and the glyph rides above the
     line it shares with the strand and the speaker beside it.

     Put it back along the pre-rotation x — the translate is applied before the
     rotation maps local +x onto screen +y, so this reads as "down" once
     turned. Written as the arithmetic rather than the 3.75px it comes to, so
     the 36px is the same 36px set on .sidebar-label svg above. (NOT a
     percentage: this <svg> carries no viewBox of its own, so the default
     transform-box:view-box has no reference box and a percentage resolves to
     nothing at all.) */
  transform: rotate(90deg) translateX(calc(36px * 2.5 / 24));
}

.sidebar-checkbox,
.fullscreen-checkbox {
  display: none;
}

.sidebar-container {
  display: flex;
  /* clip: while the size transition runs, the still-rendered content panel is
     taller/wider than the container and would extend the page's scroll range */
  overflow: clip;
  transition: width 300ms ease, height 300ms ease;
}

.left-sidebar .sidebar-container,
.right-sidebar .sidebar-container {
  flex-direction: column;
  min-height: inherit;
  width: 46px;
}

.top-bar .sidebar-container,
.bottom-bar .sidebar-container {
  flex-direction: row;
  min-width: inherit;
  /* content-sized: the top bar collapses to its 46px toggle label; the bottom
     bar to its strand band (the canvas height in voice.css, which is also
     what grows when that bar's checkbox opens) */
  height: fit-content;
}

.top-bar .sidebar-checkbox:checked ~ .sidebar-container {
  height: 100px;
}


/* --arrow-display is consumed by the arrow layer inside the #panel-toggle composite */
.sidebar-checkbox:checked ~ .sidebar-container {
  --arrow-display: inline;
}

.sidebar-label {
  color: white;
  cursor: pointer;
  /* 46px TOTAL in either box model — this page carries a global border-box
     reset, the component's page doesn't, and the collapsed bar/rail
     dimension (46px) IS this label: an implicit total would clip the icon's
     bottom wherever the container hugs the label */
  box-sizing: border-box;
  height: 46px;
  width: 36px;
  padding-top: 5px;
  padding-bottom: 5px;
  align-content: center;
  justify-items: flex-start;
}

.sidebar-label svg {
  /* block: an inline svg rides the text baseline, and its line box's descender
     space overflows the fixed-height label — visible as a page scrollbar when
     the collapsed bottom bar sits on the viewport edge */
  display: block;
  width: 36px;
  height: 36px;
  /* scale (not transform) so the hover grow composes with the per-side rotations */
  transition: scale 150ms ease, background-color 150ms ease;
}

.sidebar-label:hover svg {
  background-color: rgba(255, 255, 255, 0.15);
  scale: 1.1;
}

/* closed + hover: preview the arrow mirrored, pointing where the bar will expand */
.sidebar-checkbox:not(:checked) ~ .sidebar-container .sidebar-label:hover {
  --arrow-display: inline;
  --arrow-flip: scaleX(-1);
}

.sidebar-content {
  display: none;
  color: white;
  padding-top: 15px;
  padding-bottom: 15px;
  opacity: 0;
  transition: opacity 300ms ease, display 300ms ease allow-discrete;
}

/* direct children only: icons nested in a control (the flavor launcher below)
   carry that control's hover treatment, not their own */
.sidebar-content > svg {
  width: 36px;
  height: 36px;
  cursor: pointer;
  transition: transform 150ms ease, background-color 150ms ease;
}

.sidebar-content > svg:hover {
  background-color: rgba(255, 255, 255, 0.15);
  transform: scale(1.1);
}

/* the horizontal bars lay their content out along the bar, not down it */
.top-bar .sidebar-content,
.bottom-bar .sidebar-content {
  grid-auto-flow: column;
  align-items: start;
  gap: 4px;
}

/* one launcher per terminal flavor: icon over a name, because the flavors differ
   in what they run (shell / codex / codex --yolo), which no icon can say */
.term-flavor {
  display: grid;
  justify-items: center;
  gap: 2px;
  padding: 2px 6px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  transition: background-color 150ms ease, scale 150ms ease;
}

.term-flavor:hover {
  background-color: rgba(255, 255, 255, 0.15);
  scale: 1.1;
}

.term-flavor svg {
  width: 28px;
  height: 28px;
}

/* this one runs commands without asking — say so before it is clicked */
.term-flavor-danger {
  color: #f0b46e;
}

.sidebar-checkbox:checked ~ .sidebar-container .sidebar-content {
  display: grid;
  opacity: 1;
}

@starting-style {
  .sidebar-checkbox:checked ~ .sidebar-container .sidebar-content {
    opacity: 0;
  }
}

/* ── left sidebar: feature groups ─────────────────────────────────────────
   ONE column, disclosed in three rungs. Shut, the sidebar is the rail of
   group glyphs — the same 46px column the panel toggle stands in. Open, each
   glyph gains its name. Opening a group lists its items indented beneath it,
   in this same column rather than a second one beside it.

   Each group is a <details>, so the disclosure IS the element's own state:
   any number of groups stand open at once and no script opens them. */
.feature-rail {
  display: grid;
  grid-auto-rows: min-content;
  align-content: start;
  /* separates GROUPS, not the rows inside one. The glyphs in this set fill
     their viewBoxes to very different degrees, and the busy ones read as
     colliding when stacked flush. */
  gap: 10px;
  /* symmetric, and the bottom half is load-bearing: the hover scale grows a
     glyph 1.8px past its own box, and on the LAST row that lands outside the
     rail's content edge — transformed overflow counts toward scrollable
     overflow, so overflow-y: auto answered it with a scrollbar. Every other
     row's overhang falls inside the content that follows it. */
  padding-top: 5px;
  padding-bottom: 5px;
  color: white;
  min-height: 0;
  /* the names run past the shut rail's 46px, so clip on x — left to compute
     from overflow-y, they would come back as a horizontal scrollbar — and
     scroll on y once enough groups stand open to overrun the grid row */
  overflow-x: hidden;
  overflow-y: auto;
}

/* the glyph is the disclosure marker, so the default triangle goes; both
   rails' group rows share this shape */
.feature-group summary,
.resource-group summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  /* containing block for the shut-state overlay below */
  position: relative;
}

/* Shut, this covers the whole row and turns a glyph click into "open the
   sidebar". Without it the click reaches the summary and toggles a disclosure
   that is clipped out of sight: nothing moves, yet the group has silently
   changed state. Clicking a <label> runs the LABEL's activation behaviour and
   not the summary's, so the disclosure is left alone and opening the sidebar
   is the entire effect — which is what a glyph you cannot read should do.
   Once open the overlay is gone and the summary is the disclosure again. */
.rail-expand {
  position: absolute;
  inset: 0;
  cursor: pointer;
  /* above the glyph, which the hover `scale` turns into a stacking context of
     its own — and it comes later in the box tree, so without this it paints
     over the overlay and takes the click back the moment you hover, which is
     always. */
  z-index: 1;
}

.left-sidebar .sidebar-checkbox:checked ~ .sidebar-container .rail-expand,
.right-sidebar .sidebar-checkbox:checked ~ .sidebar-container .rail-expand {
  display: none;
}

.feature-group summary::-webkit-details-marker,
.resource-group summary::-webkit-details-marker {
  display: none;
}

/* the row's label, printed from the same title attribute that is the shut
   glyph's tooltip — one string, so the two can never disagree */
.feature-group summary::after,
.resource-group summary::after {
  content: attr(title);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.feature-group summary svg {
  display: block;
  width: 36px;
  height: 36px;
  flex: none;
  transition: scale 150ms ease, background-color 150ms ease;
}

.feature-group summary:hover svg {
  background-color: rgba(255, 255, 255, 0.15);
  scale: 1.1;
}

/* an open group holds the chip, so the column says which groups are listed
   below — the <details> state carries what a class used to. Scoped to the
   sidebar being open, because a shut rail shows no items for the chip to
   point at; re-opening restores it with no state of its own. */
.left-sidebar .sidebar-checkbox:checked ~ .sidebar-container
  .feature-group[open] > summary svg {
  background-color: rgba(255, 255, 255, 0.15);
}

.feature-items {
  display: grid;
  gap: 2px;
  margin: 4px 0 0;
  /* the slight indent that puts the items under their group rather than
     beside it, without spending enough width to matter */
  padding: 0 0 0 10px;
  list-style: none;
}

/* A shut rail is a column of glyphs and nothing else. Clipping alone does not
   get there: the name begins 44px in and would show a 2px sliver of its first
   letter past the 46px edge, and an open group's items would still hold their
   vertical space and gap the glyphs apart. Re-opening restores both. */
.left-sidebar .sidebar-checkbox:not(:checked) ~ .sidebar-container .feature-items,
.left-sidebar .sidebar-checkbox:not(:checked) ~ .sidebar-container
  .feature-group summary::after,
.right-sidebar .sidebar-checkbox:not(:checked) ~ .sidebar-container
  .resource-group summary::after {
  display: none;
}

.feature-item {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 4px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  text-align: left;
  cursor: pointer;
  transition: background-color 150ms ease;
}

.feature-item:hover {
  background-color: rgba(255, 255, 255, 0.15);
}

/* the glyph grows on hover, not the row: a full-width row that scales
   overruns the column it sits in */
.feature-item svg {
  display: block;
  width: 24px;
  height: 24px;
  transition: scale 150ms ease;
}

.feature-item:hover svg {
  scale: 1.1;
}

.feature-item span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.left-sidebar .sidebar-container {
  /* clamp to the grid row, so overflow lands in the rail's own scroller */
  max-height: 100%;
  /* one column holding both row shapes: the 36px group glyph beside its
     name, and the indented 24px item glyph beside its label */
  --feature-col-w: 8rem;
}

.left-sidebar .sidebar-checkbox:checked ~ .sidebar-container {
  width: var(--feature-col-w);
}

/* ── right sidebar: resource types ────────────────────────────────────────
   The left rail's grammar: one <details> per type, the summary as the rail
   row — glyph, name from the title attribute, the type's own controls — and
   the list as the disclosure's content in the same column. The disclosure IS
   the element's own state; the script's only tie to it is fetching a list
   when a group opens. */
.resource-rail {
  display: grid;
  grid-auto-rows: min-content;
  align-content: start;
  /* separates GROUPS, not the rows inside one */
  gap: 10px;
  /* symmetric, and the bottom half is load-bearing: the hover scale grows a
     glyph past its own box, and on the LAST row that lands outside the
     rail's content edge — transformed overflow counts toward scrollable
     overflow, so overflow-y: auto answers it with a scrollbar. */
  padding-top: 5px;
  padding-bottom: 5px;
  color: white;
  min-height: 0;
  /* names and lists run past the shut rail's 46px, so clip on x — left to
     compute from overflow-y, they would come back as a horizontal scrollbar
     — and scroll on y once enough lists stand open to overrun the grid row */
  overflow-x: hidden;
  overflow-y: auto;
}

/* the file input is never seen: its label is the control, and clicking that
   opens the picker with no script involved */
.resource-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.resource-add {
  display: none;
  cursor: pointer;
  /* after the summary's ::after name in the row: the pseudo is the last
     order-0 flex item, so any positive order lands past it */
  order: 1;
}

.right-sidebar .sidebar-checkbox:checked ~ .sidebar-container .resource-add {
  display: block;
}

.resource-add svg {
  display: block;
  width: 28px;
  height: 28px;
  transition: scale 150ms ease, background-color 150ms ease;
}

.resource-add:hover svg {
  background-color: rgba(255, 255, 255, 0.15);
  scale: 1.1;
}

.resource-file:focus-visible ~ .resource-add svg {
  outline: 2px solid rgba(140, 120, 255, 0.7);
  outline-offset: 2px;
}

/* direct child only: the add control's 28px svg rides the same summary and
   keeps its own size and hover treatment */
.resource-group summary > svg {
  display: block;
  width: 36px;
  height: 36px;
  flex: none;
  transition: scale 150ms ease, background-color 150ms ease;
}

.resource-group summary:hover > svg {
  background-color: rgba(255, 255, 255, 0.15);
  scale: 1.1;
}

/* an open group holds the chip, so the column says which lists are standing
   below — the <details> state carries what a class used to. Scoped to the
   sidebar being open, because a shut rail shows no list for the chip to
   point at; re-opening restores it with no state of its own. */
.right-sidebar .sidebar-checkbox:checked ~ .sidebar-container
  .resource-group[open] > summary > svg {
  background-color: rgba(255, 255, 255, 0.15);
}

.right-sidebar .sidebar-container {
  max-height: 100%;
}

.right-sidebar .sidebar-checkbox:checked ~ .sidebar-container {
  width: 17rem;
}

/* A list is gated twice: a closed <details> renders no content, and a shut
   sidebar hides every .sidebar-content. Overflow is the rail's — the whole
   column scrolls as one, like the left sidebar's. */
.resource-panel {
  width: 17rem;
  box-sizing: border-box;
  padding: 8px;
  align-content: start;
  gap: 6px;
  /* an open list scrolls inside its own cap rather than burying the group
     rows below it: however long a list grows, the next type's row stays a
     glance away. The rail's own scroller remains the fallback when several
     capped lists together overrun the column. */
  max-height: 50vh;
  overflow-y: auto;
  /* lets the height below interpolate from auto; where unsupported the
     height snaps at the start of the collapse and the cap still glides */
  interpolate-size: allow-keywords;
  /* the generic .sidebar-content pair, plus the vertical sizes: display
     holds until the fade ends, so the sizes must fall over those same
     300ms — otherwise the panel keeps its full height for the whole width
     transition and surrenders it in one frame when display flips, and the
     group rows below jump. */
  transition: opacity 300ms ease, display 300ms ease allow-discrete,
      height 300ms ease, max-height 300ms ease, padding 300ms ease;
}

/* Shut, both vertical bounds fall to zero together. Whichever binds — height
   for a list shorter than the cap, max-height for one at it — is the panel's
   used size, and each is a straight run to zero, so the column settles
   through the whole 300ms the rail's width takes, never in a last-frame
   jump. padding-block goes too: a border-box height of 0 still stands 16px
   tall while the padding remains. */
.right-sidebar .sidebar-checkbox:not(:checked) ~ .sidebar-container .resource-panel {
  height: 0;
  max-height: 0;
  padding-block: 0;
  /* clip, not the resting auto: a shrinking box otherwise grows a scrollbar
     the moment its height passes under the content's */
  overflow: clip;
}

/* opening mirrors the collapse: lists grow over the same 300ms the rail
   widens, instead of standing at full height in the first frame */
@starting-style {
  .right-sidebar .sidebar-checkbox:checked ~ .sidebar-container .resource-panel {
    height: 0;
    max-height: 0;
    padding-block: 0;
  }
}

.resource-list {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.resource-item {
  border: 1px dotted rgba(255, 255, 255, 0.4);
  border-radius: 5px;
}

.resource-item:hover {
  border-style: solid;
}

.resource-link {
  display: block;
  padding: 6px 8px;
  color: inherit;
  text-decoration: none;
}

.resource-item time {
  font-size: 11px;
  opacity: 0.7;
}
/* How many turns a conversation holds, from the listing's own metadata: a
   count the write stamped, so no body is fetched to show it. It shares the
   time's line — right-aligned against it — and an item whose type stamps no
   count leaves the slot empty, which is how it disappears. */
.resource-item time {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}

.resource-count:empty { display: none; }

.resource-count {
  flex: none;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}


.resource-item p {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.resource-status {
  margin: 0;
  font-size: 12px;
  opacity: 0.7;
}

/* The foot of a paged list, standing only while the panel carries the next
   page's token: the stamped attribute is the whole state, and the button's
   presence IS the statement that older items exist. */
.resource-more {
  justify-self: start;
  padding: 4px 8px;
  border: 1px dotted rgba(255, 255, 255, 0.4);
  border-radius: 5px;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.resource-more:hover {
  border-style: solid;
}

.resource-panel:not([data-next-page]) .resource-more {
  display: none;
}

/* The status line reads the panel's stamped data-state: each enumerated
   state prints its own message here, so the script never composes one — the
   free-form text the script does set (error detail, upload tallies) rides
   the element's own textContent instead of a ::before. Ready needs no line
   at all: the list is the message. */
.resource-panel[data-state="ready"] .resource-status {
  display: none;
}

.resource-panel[data-state="loading"] .resource-status::before {
  content: "loading…";
}

.resource-panel[data-state="empty"] .resource-status::before {
  content: "nothing stored yet";
}

.resource-panel[data-state="unauthorized"] .resource-status::before {
  content: "sign in to see stored items";
}

.resource-panel[data-state="unreachable"] .resource-status::before {
  content: "backend unreachable";
}
