/* ── newtownvillage — le portail familial ─────────────────────────────
   La base est la feuille de la vitrine (/styles.css, servie aux deux sites :
   jetons, en-tête, tuiles, pastilles, boutons). Ici, tout ce qui est propre au
   portail : la porte, les tuiles qui s'ouvrent, le mode contrôle (sidebar,
   barre d'outils, scène), les transitions — repris tels quels de la vitrine
   quand sa console a déménagé ici (ADR 0001) —, puis l'écran « télévision ». */

:root {
  /* L'« écran » du mode contrôle : gouttière autour du service et arrondi.
     La barre d'outils s'aligne sur la même gouttière. */
  --screen-gap: 14px;
  --screen-radius: 14px;
}

.foot-key {
  border: 0;
  background: none;
  font: inherit;
  color: var(--faint);
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 6px;
  transition: color 0.18s ease;
}
.foot-key:hover { color: var(--text); }

/* .open : le nom devient un lien, étiré sur toute la tuile — une seule cible,
   mais un vrai lien tabulable. */
.tile.open { cursor: pointer; }
.tile.open:hover {
  border-color: var(--border-strong);
  background: var(--panel-hover);
}
.tile-a {
  color: inherit;
  text-decoration: none;
}
.tile-a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.tile.open .icon { color: var(--accent); }
.tile.open:hover .go { transform: translateX(3px); color: var(--text); }
.go {
  margin-left: auto;
  align-self: center;
  color: var(--faint);
  font-size: 1.05rem;
  transition: transform 0.2s ease, color 0.2s ease;
}

/* ── Ouverture de session ──────────────────────────────────────────── */

.login {
  margin: auto;
  width: min(400px, calc(100vw - 32px));
  padding: 28px;
  border: 1px solid var(--border-strong);
  border-radius: 16px;
  background: var(--bg-lift);
  color: var(--text);
}
.login::backdrop {
  background: rgba(4, 5, 8, 0.72);
  backdrop-filter: blur(3px);
}
.login h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.4rem;
}
.login-lede {
  margin-top: 6px;
  font-size: 0.9rem;
  color: var(--faint);
}
.login-field {
  display: block;
  margin-top: 22px;
}
.login-field span {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 8px;
}
.login-field input {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.28);
  color: var(--text);
  font: inherit;
  font-family: var(--mono);
}
.login-field input:focus-visible { border-color: var(--accent); }
.login-error {
  margin-top: 12px;
  font-size: 0.85rem;
  color: var(--down);
}
.login-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 24px;
}

/* ── Mode contrôle ─────────────────────────────────────────────────────
   Couche fixe au-dessus de la page, révélée par body.app. Cachée en OPACITÉ (ni
   display:none, ni visibility) pour que les iframes gardent leur taille et leur
   état ; l'attribut inert la sort du clavier quand elle est invisible. */

.app-layer {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  background: var(--bg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}
body.app { overflow: hidden; }
body.app .app-layer { opacity: 1; pointer-events: auto; }

/* Ni filet ni fond propre : sidebar, barre d'outils et scène forment une seule
   surface. Tout le contraste est porté par l'écran du service, qui flotte dessus. */
.sidebar {
  width: 264px;
  flex: none;
  display: flex;
  flex-direction: column;
}
.side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 18px 14px 12px;
}
.side-brand {
  font-family: var(--mono);
  font-size: 1.05rem;
  font-weight: 700;
  text-decoration: none;
  color: inherit;
  border-radius: 8px;
}
.side-pill { padding: 4px 9px; font-size: 0.66rem; }

.side-nav {
  flex: 1;
  overflow-y: auto;
  padding: 4px 10px 12px;
}
.side-title {
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 0 10px;
  margin: 16px 0 6px;
}
.side-group:first-child .side-title { margin-top: 6px; }

.side-row {
  display: flex;
  align-items: center;
  gap: 2px;
}
.side-item {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: 10px;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.88rem;
  transition: background 0.15s ease, color 0.15s ease;
}
.side-item:hover { background: var(--panel-hover); color: var(--text); }
.side-item.active {
  background: var(--panel-hover);
  border-color: var(--border);
  color: var(--text);
}
.side-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
}
.side-icon svg { width: 17px; height: 17px; }
.side-item.active .side-icon { color: var(--accent); }
.side-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.side-item .dot { position: static; margin-left: auto; flex: none; }

.side-close {
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--faint);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.side-close:hover { background: var(--panel-hover); color: var(--text); }

.side-foot {
  flex: none;
  padding: 10px;
}

/* Zone principale : barre d'outils + scène */
.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.toolbar {
  flex: none;
  height: 52px;
  display: flex;
  align-items: center;
  gap: 10px;
  /* Aligné sur les gouttières de l'écran (--screen-gap) : la pastille commence
     au bord gauche du cadre, les boutons finissent à son bord droit. */
  padding: 0 calc(var(--screen-gap) - 8px) 0 var(--screen-gap);
}
.toolbar .dot { position: static; flex: none; }
.tool-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 650;
  font-size: 0.95rem;
  white-space: nowrap;
}
.tool-meta {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tool-spacer { flex: 1; }
.tool-btn {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.tool-btn svg { width: 18px; height: 18px; }
.tool-btn:hover {
  background: var(--panel-hover);
  border-color: var(--border);
  color: var(--text);
}
.side-toggle { display: none; }

/* Les iframes restent montées, seule l'active est visible. Chaque service est
   posé dans un « écran » : marge, bord fin, coins arrondis. */
.stage {
  position: relative;
  flex: 1;
  min-height: 0;
}
.frame-wrap {
  position: absolute;
  /* inset et non un padding sur .stage : une boîte absolue se cale sur la
     boîte de padding de son parent, un padding ne la décalerait donc pas. */
  inset: var(--screen-gap);
  border: 1px solid var(--border);
  border-radius: var(--screen-radius);
  background: var(--bg-lift);
  box-shadow: 0 22px 60px -30px rgba(0, 0, 0, 0.9),
              inset 0 1px 0 rgba(255, 255, 255, 0.04);
  overflow: hidden;
  /* Force une couche de compositing : sans elle, Safari laisse l'iframe
     déborder des coins arrondis du parent. */
  transform: translateZ(0);
  visibility: hidden;
}
.frame-wrap.active { visibility: visible; }
.frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  /* Arrondi porté aussi par l'iframe : ceinture et bretelles pour le clipping. */
  border-radius: calc(var(--screen-radius) - 1px);
  background: var(--bg-lift);
}
.frame-loading {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--faint);
}

/* Service qui refuse l'intégration */
.frame-panel {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 14px;
  text-align: center;
  padding: 30px;
}
.frame-panel h2 { font-size: 1.15rem; letter-spacing: -0.01em; }
.frame-panel p { max-width: 48ch; color: var(--muted); font-size: 0.92rem; }
.frame-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Sidebar en tiroir sur petit écran */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: 65;
  background: rgba(4, 6, 12, 0.55);
}
@media (max-width: 860px) {
  :root { --screen-gap: 10px; --screen-radius: 12px; }
  body.app .side-toggle { display: grid; }
  .app-layer .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 70;
    width: min(300px, 84vw);
    /* En tiroir, la sidebar passe par-dessus l'écran : il lui faut un fond,
       contrairement au mode large où elle partage la surface du dashboard. */
    background: rgba(14, 16, 21, 0.96);
    backdrop-filter: blur(14px);
    transform: translateX(-105%);
    /* visibility, pas seulement translate : hors écran, le tiroir doit sortir
       de la tabulation et du lecteur d'écran. Retardée pour laisser glisser. */
    visibility: hidden;
    transition: transform 0.22s ease, visibility 0s 0.22s;
  }
  .app-layer .sidebar.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.22s ease;
    box-shadow: 24px 0 60px -30px rgba(0, 0, 0, 0.8);
  }
}
/* Le voile n'appartient qu'au tiroir : jamais de résidu en large. */
@media (min-width: 861px) {
  .backdrop { display: none; }
}

/* ── Transitions de vue ───────────────────────────────────────────────
   Toute bascule passe par morph() dans app.js. Registre pauvre : une durée, une
   courbe ; on ne s'en éloigne que là où le mouvement est ample.

   L'aiguillage se fait par data-morph sur <html>, et non par l'option `types` de
   l'API : elle est trop récente dans Firefox pour qu'on en dépende. */

:root {
  --morph-ms: 240ms;
  --morph-ease: cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* Le groupe porte le tempo ; old/new en héritent (règle du navigateur :
   animation-duration: inherit). On ne redéclare l'un ou l'autre que pour
   changer l'animation elle-même, jamais sa durée. */
::view-transition-group(*) {
  animation-duration: var(--morph-ms);
  animation-timing-function: var(--morph-ease);
}
/* Le fond ne doit pas se traîner derrière ce qui bouge. */
::view-transition-group(root) { animation-duration: 180ms; }

/* 1 ─ Ouverture de session : les tuiles en place glissent vers leur nouvelle
   position (le groupe s'en charge seul), les nouvelles entrent. :only-child = la
   paire n'a qu'un enfant, donc une entrée ou une sortie, pas un morphing. */
html[data-morph="unlock"] ::view-transition-new(*):only-child {
  animation: vt-rise var(--morph-ms) var(--morph-ease) both;
}
html[data-morph="unlock"] ::view-transition-old(*):only-child {
  animation: vt-sink 150ms var(--morph-ease) both;
}
@keyframes vt-rise { from { opacity: 0; transform: translateY(10px) scale(0.985); } }
@keyframes vt-sink { to   { opacity: 0; transform: translateY(-6px) scale(0.99);  } }

/* 2 ─ La tuile DEVIENT l'écran. Un seul porteur du nom « screen » à chaque
   instant : deux porteurs simultanés font abandonner la transition, en silence.
   Les deux sélecteurs sont donc mutuellement exclusifs, et le garde body.app est
   VITAL — .app-layer reste rendue (opacity:0, pas display:none), or l'opacité
   d'un ancêtre est ignorée lors de la capture : sans lui, l'écran surgirait en
   fantôme opaque par-dessus la page. */
body:not(.app) #board [data-morph-target] { view-transition-name: screen; }
body.app .frame-wrap.active               { view-transition-name: screen; }

::view-transition-group(screen) { animation-duration: 300ms; z-index: 2; }
/* Fondu simple, sans translation : c'est le GROUPE qui porte le mouvement. La
   capture ancienne disparaît vite — son étirement à la taille d'un écran n'a
   ainsi pas le temps de se voir. */
::view-transition-old(screen) { animation: vt-fade-out 110ms linear both; }
::view-transition-new(screen) { animation: vt-fade-in 200ms 70ms linear both; }
@keyframes vt-fade-out { to   { opacity: 0; } }
@keyframes vt-fade-in  { from { opacity: 0; } }

/* 3 ─ La couche « new » est VIVANTE pendant la transition : une animation
   d'entrée s'y rejouerait par-dessus le fondu du groupe. */
:active-view-transition .tile { animation: none; }

@media (prefers-reduced-motion: reduce) {
  /* « * » ne matche que des ÉLÉMENTS : les pseudo-éléments de la transition
     forment un arbre à part et échappent à la règle ci-dessus. Ceinture — les
     bretelles sont le garde REDUCED dans morph(). */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ── Propre au portail ─────────────────────────────────────────────── */

:root {
  /* Le liseré de l'écran : fin, à gauche et en bas seulement — en haut, la
     barre d'outils tient déjà la place, à droite, l'écran va jusqu'au bord.
     Le service garde ainsi presque toute la surface. */
  --bezel: 6px;
  --screen-radius: 16px;
}

/* ── La porte ──────────────────────────────────────────────────────────── */

.gate {
  display: grid;
  place-items: center;
  min-height: 62vh;
}
/* .login vient de la vitrine (le dialogue) : ici c'est une carte posée dans la
   page, sans marge automatique. */
.gate-card { margin: 0; }
.gate-card h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.2;
}

/* ── Le terrain ────────────────────────────────────────────────────────── */

.portail .top { align-items: center; }
.portail main { padding-top: 40px; padding-bottom: 48px; }
.portail .board-group { padding-top: 34px; }
.portail .board-group:first-child { padding-top: 0; }
.portail .zone-title { margin-bottom: 18px; }

/* Les tuiles des conteneurs sans interface : de la plomberie, en retrait. */
.tile.no-ui { opacity: 0.78; }
.tile.no-ui .desc { font-weight: 500; color: var(--muted); }

/* ── L'écran « télévision » ────────────────────────────────────────────
   Le service occupe toute la scène, sous la barre d'outils : un liseré fin à
   gauche et en bas, de grands coins arrondis. C'est le CADRE qui arrondit et
   qui rogne (overflow:hidden, couche de compositing) — pas l'iframe : un
   service en plein écran, ou qui peint jusqu'au bord, déborderait sinon dans
   les angles. */
.portail .frame-wrap {
  inset: 0 0 var(--bezel) var(--bezel);
  border-radius: var(--screen-radius);
  border-color: var(--border-strong);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05),
              0 0 0 1px rgba(0, 0, 0, 0.35);
}
.portail .frame { border-radius: calc(var(--screen-radius) - 1px); }
.portail .toolbar { padding-left: calc(var(--bezel) + 8px); }

@media (max-width: 860px) {
  :root { --bezel: 4px; --screen-radius: 12px; }
}

/* Imprimé, le portail ne garde que le terrain : ni la couche du mode
   contrôle, ni les boutons. */
@media print {
  .app-layer, .foot-key { display: none !important; }
}
