/* =====================================================================
   jude.css — the whole design system. One file, no build step.

   Written phone first. Every rule below is the 390x844 rule; the desk is
   one @media (min-width:700px) block at the end that is allowed to add,
   never to rescue. The last front end was written the other way round and
   that is why the headline shredded into nine lines on a phone.

   The colour law (BRIEF.md), as proportions and not decoration:
     40%  light surface        .slide default and [data-kind="light"]
     35%  the signature cyan   .slide[data-kind="hero"]
     20%  deep navy            .slide[data-kind="deep"]
      5%  accent and status
   --erling is intervention. It appears on [data-kind="intervene"] and
   nowhere else in this file, ever.
   ===================================================================== */

:root{
  /* ---- surfaces: the 40% ---- */
  --paper:#ffffff;
  --ink:#0b1014;
  --ink-2:#4c5457;
  --rule:#d9dee1;

  /* ---- the signature gradient: the 35%. These three stops are the law. ---- */
  --grad-0:#064163;
  --grad-1:#0a689c;
  --grad-2:#18a3e9;              /* the lighter end: the one that carries */
  --grad:linear-gradient(155deg,#064163 0%,#0a689c 55%,#18a3e9 100%);

  /* ---- deep: the 20% ---- */
  --deep:#071722;
  --on-deep:#ffffff;
  --on-deep-2:#9fb2bd;

  /* ---- accent and status: the 5% ---- */
  --accent:#0a689c;
  --cyan:#18a3e9;
  --erling:#c8102e;              /* intervention only */
  --focus:#ffdd00;

  /* ---- one family, the system stack ---- */
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

  /* ---- five type sizes and no more (docs/FRONT.md) ---- */
  --t-figure:clamp(3rem,14vw,7rem);       /* the number          */
  --t-title:clamp(1.6rem,7vw,3rem);       /* the words           */
  --t-line:clamp(1rem,4.4vw,1.15rem);     /* the haiku           */
  --t-label:0.72rem;                      /* what the number is  */
  --t-button:1rem;                        /* the verb            */

  /* ---- metrics ---- */
  --tap:48px;                             /* never smaller, anywhere */
  --gut:20px;
  --mast:56px;

  /* ---- the phone's own edges, paid back so nothing hides under them ---- */
  --safe-t:env(safe-area-inset-top,0px);
  --safe-r:env(safe-area-inset-right,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --safe-l:env(safe-area-inset-left,0px);

  color-scheme:light;
}

*{box-sizing:border-box;min-width:0}

html,body{
  height:100%;
  overflow:hidden;                        /* the law */
  overscroll-behavior:none;
}
body{
  margin:0;
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  background:var(--paper);
  color:var(--ink);
  font:400 100%/1.3 var(--sans);
  -webkit-text-size-adjust:100%;
  -webkit-font-smoothing:antialiased;
  touch-action:none;                      /* we read the gestures ourselves */
}
/* min() so the dynamic viewport can shrink the page but never grow it past
   the box html already gave us — that is what keeps html from scrolling. */
@supports (height:100dvh){ body{ height:min(100%,100dvh) } }

/* =====================================================================
   The mast: the wordmark, and at most one action.
   ===================================================================== */
.mast{
  height:calc(var(--mast) + var(--safe-t));
  padding:var(--safe-t) calc(var(--gut) + var(--safe-r)) 0 calc(var(--gut) + var(--safe-l));
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  background:var(--deep);color:var(--on-deep);
}
/* The wordmark is /ui/wordmark.svg carried on a mask, so it is one file,
   it takes currentColor, and its box is always 118:22 — it cannot wrap. */
.mark{
  display:block;flex:0 0 auto;
  height:17.5px;aspect-ratio:59/11;        /* 118:22, exactly, at any size */
  background:currentColor;
  -webkit-mask:url("/apps/ui/wordmark.svg") no-repeat center/contain;
  mask:url("/apps/ui/wordmark.svg") no-repeat center/contain;
}
.mast__back{
  flex:0 0 auto;
  min-height:var(--tap);min-width:var(--tap);
  padding:0 14px;margin-right:-14px;
  border:0;background:none;color:inherit;
  font:600 var(--t-button)/1 var(--sans);
  cursor:pointer;
}
.mast__back::before{content:"\2190\00a0"}  /* an arrow, then the word */

/* =====================================================================
   The stage holds exactly one slide, and the slide is the viewport.
   ===================================================================== */
#stage{
  position:relative;
  min-height:0;
  overflow:hidden;                        /* the law */
}

.slide{
  position:absolute;inset:0;
  display:flex;flex-direction:column;
  padding:16px calc(var(--gut) + var(--safe-r)) 8px calc(var(--gut) + var(--safe-l));
  overflow:hidden;                        /* the law, per slide */
  background:var(--paper);color:var(--ink);
}

/* ---- hero: the 35%, the gradient, exactly as written ---- */
.slide[data-kind="hero"]{background:var(--grad);color:var(--on-deep)}
.slide[data-kind="hero"] .slide__label,
.slide[data-kind="hero"] .slide__line{color:rgba(255,255,255,.88)}

/* ---- deep: the 20% ---- */
.slide[data-kind="deep"]{background:var(--deep);color:var(--on-deep)}
.slide[data-kind="deep"] .slide__label,
.slide[data-kind="deep"] .slide__line{color:var(--on-deep-2)}

/* ---- intervene: a red keyline and a red figure. Nothing else is red. ---- */
.slide[data-kind="intervene"]{
  background:var(--paper);color:var(--ink);
  box-shadow:inset 0 0 0 3px var(--erling);
}
.slide[data-kind="intervene"] .slide__figure,
.slide[data-kind="intervene"] .slide__title{color:var(--erling)}
.slide[data-kind="intervene"] .slide__label{color:var(--erling)}

/* =====================================================================
   The parts, in reading order: label, figure, title, line. They live in
   one .slide__head so the desk can set them beside a list in one rule.
   Nothing is centred that has to be read.
   ===================================================================== */
.slide__head{
  flex:0 1 auto;min-height:0;
  display:flex;flex-direction:column;align-items:flex-start;
}
.slide__label{
  flex:0 0 auto;margin:0 0 10px;
  font-size:var(--t-label);font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-2);
  overflow-wrap:anywhere;
}

/* The figure is the answer. When it opens it is a doorway, not a label. */
.slide__figure{
  flex:0 0 auto;margin:0;
  font-size:var(--t-figure);font-weight:800;
  letter-spacing:-.04em;line-height:.9;
  font-variant-numeric:tabular-nums;
  overflow-wrap:anywhere;
}
button.slide__figure{
  display:block;width:100%;text-align:left;
  min-height:var(--tap);
  padding:0;border:0;border-bottom:3px solid transparent;
  background:none;color:inherit;font-family:inherit;cursor:pointer;
}
button.slide__figure .slide__num::after{content:"\00a0\2192";font-size:.4em;vertical-align:.55em;color:var(--cyan)}
.slide[data-kind="hero"] button.slide__figure .slide__num::after,
.slide[data-kind="deep"] button.slide__figure .slide__num::after{color:currentColor}
button.slide__figure:hover{border-bottom-color:currentColor}

.slide__unit{
  display:block;margin-top:.4em;
  font-size:var(--t-line);font-weight:400;letter-spacing:0;
  color:var(--ink-2);
}
.slide[data-kind="hero"] .slide__unit,
.slide[data-kind="deep"] .slide__unit{color:inherit;opacity:.85}

/* 16ch is what stopped the headline shredding into nine lines. */
.slide__title{
  flex:0 0 auto;margin:.15em 0 0;max-width:16ch;
  font-size:var(--t-title);font-weight:800;
  letter-spacing:-.03em;line-height:1.04;
  overflow-wrap:anywhere;text-wrap:balance;
}
.slide__line{
  flex:0 0 auto;margin:.6em 0 0;max-width:30ch;
  font-size:var(--t-line);line-height:1.35;
  color:var(--ink-2);
  overflow-wrap:anywhere;
}

/* The field on the arrival slide: one line, full width, never off the edge. */
.slide__ask{
  display:block;width:100%;margin:.55em 0 0;
  min-height:var(--tap);padding:0;
  font:800 var(--t-title)/1.1 var(--sans);letter-spacing:-.03em;
  color:inherit;background:none;
  border:0;border-bottom:3px solid currentColor;border-radius:0;
  outline-offset:6px;
}
.slide__ask::placeholder{color:currentColor;opacity:.45}

/* =====================================================================
   Rows. A long list is not a scroller; it is slides of rows.
   ===================================================================== */
.rows{
  list-style:none;margin:14px 0 0;padding:0;
  flex:0 1 auto;min-height:0;
  overflow:hidden;
}
.row{border-top:1px solid var(--rule)}
.row:last-child{border-bottom:1px solid var(--rule)}
.row > *{
  display:flex;align-items:center;gap:12px;
  width:100%;min-height:var(--tap);
  padding:6px 0;margin:0;
  border:0;background:none;color:inherit;
  font:inherit;font-size:1rem;text-align:left;
}
.row button{cursor:pointer}
.row__name{flex:1 1 auto;font-weight:600;overflow-wrap:anywhere}
.row__number{flex:0 0 auto;font-weight:800;font-variant-numeric:tabular-nums}
.row button .row__number{color:var(--accent)}
.slide[data-kind="hero"] .row,
.slide[data-kind="deep"] .row{border-color:rgba(255,255,255,.2)}
.slide[data-kind="hero"] .row button .row__number,
.slide[data-kind="deep"] .row button .row__number{color:#8fd8ff}

/* A slide that has been split says how many are waiting. It never hides. */
.more{
  flex:0 0 auto;margin:10px 0 0;
  font-size:var(--t-label);font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-2);
}
.slide[data-kind="hero"] .more,
.slide[data-kind="deep"] .more{color:inherit;opacity:.85}

/* =====================================================================
   The one verb, in the bottom third, where the thumb lives.
   ===================================================================== */
.bar{
  flex:0 0 auto;
  margin-top:auto;                        /* pushed to the bottom */
  padding-top:16px;
  display:flex;
}
.verb{
  min-height:var(--tap);min-width:var(--tap);
  width:100%;padding:0 22px;
  display:inline-flex;align-items:center;justify-content:center;
  font:600 var(--t-button)/1 var(--sans);
  border:2px solid var(--deep);border-radius:999px;
  background:var(--deep);color:var(--paper);
  cursor:pointer;
}
.slide[data-kind="hero"] .verb,
.slide[data-kind="deep"] .verb{background:#fff;border-color:#fff;color:var(--deep)}
.slide[data-kind="intervene"] .verb{background:var(--erling);border-color:var(--erling);color:#fff}
.verb[aria-busy="true"]{opacity:.6;pointer-events:none}

/* =====================================================================
   Where you are. A strip of dots, never a scrollbar.
   ===================================================================== */
.dots{
  min-height:calc(22px + var(--safe-b));
  padding:8px calc(var(--gut) + var(--safe-r)) calc(8px + var(--safe-b)) calc(var(--gut) + var(--safe-l));
  display:flex;align-items:center;gap:6px;
  background:var(--paper);color:var(--ink);
}
.dots i{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.24}
.dots i[data-on]{width:20px;border-radius:3px;opacity:1}

/* Every control is reachable by Tab, and you can see where you are. */
:where(button,input,[tabindex]):focus-visible{
  outline:3px solid var(--focus);outline-offset:3px;
}
.slide:focus{outline:none}

/* =====================================================================
   fit() steps the scale down before it ever considers hiding a thing.
   ===================================================================== */
.slide[data-fit="1"]{--t-figure:clamp(2.25rem,10vw,5rem);--t-title:clamp(1.35rem,5.4vw,2.25rem)}
.slide[data-fit="2"]{--t-figure:clamp(1.75rem,7.5vw,3.5rem);--t-title:clamp(1.15rem,4.4vw,1.75rem);
                     --t-line:clamp(.9rem,3.6vw,1rem)}
.slide[data-fit="3"]{--t-figure:clamp(1.4rem,5.6vw,2.5rem);--t-title:1.05rem;
                     --t-line:.9rem;--tap:48px}
.slide[data-clip] .slide__line{display:none}

/* =====================================================================
   Motion, and its absence.
   ===================================================================== */
@media (prefers-reduced-motion:no-preference){
  .slide{animation:in .2s ease both}
  @keyframes in{from{opacity:0}to{opacity:1}}
  button.slide__figure,.verb,.mast__back{transition:background-color .18s ease,color .18s ease,border-color .18s ease}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
}

/* =====================================================================
   The desk. Only from here may anything grow. The phone rules above are
   the truth; these add room, they never rescue a broken layout.
   ===================================================================== */
@media (min-width:700px){
  :root{ --gut:48px; --mast:64px; --t-label:0.78rem; --t-button:1.0625rem }
  .mark{height:22px}
  .slide{padding:32px calc(var(--gut) + var(--safe-r)) 20px calc(var(--gut) + var(--safe-l))}
  .slide__title{max-width:22ch}
  .slide__line{max-width:40ch}
  /* The figure and the list sit side by side once there is room for both. */
  .slide:has(.rows){
    display:grid;
    grid-template-columns:minmax(0,7fr) minmax(0,9fr);
    grid-template-rows:minmax(0,1fr) auto;
    grid-template-areas:"head list" "bar more";
    column-gap:56px;
  }
  .slide:has(.rows) .slide__head{grid-area:head;justify-content:center}
  .slide:has(.rows) .rows{grid-area:list;margin-top:0}
  .slide:has(.rows) .more{grid-area:more;justify-self:end;align-self:center;margin:0}
  .slide:has(.rows) .bar{grid-area:bar;margin-top:0;align-self:end}
  .verb{width:auto}
  .dots{min-height:calc(28px + var(--safe-b))}
}

/* ---- the vertical composition ----

   A slide was letting its words sit at the top and leaving the rest of the
   phone empty; on an 844-pixel screen that is six hundred pixels of nothing.
   A slide is a composition, so the block of words is centred in the space it
   has, and a verb still sits in the bottom third where a thumb reaches. When
   there is a list, the list takes the room and the words stay at the top,
   because a list read from the middle of a screen is worse than one read from
   the top. */
.slide{ justify-content:center; }
.slide:has(.rows),
.slide:has(.slide__rows){ justify-content:flex-start; }
.slide__head{ flex:0 0 auto; }
.slide__verb,
.slide__do{ margin-top:auto; }

/* On the desk there is room to breathe, so the words hold to the upper third
   and never drift to the middle of a wide screen. */
@media (min-width:700px){
  .slide{ justify-content:flex-start; padding-top:clamp(24px,7vh,72px); }
  .slide__verb,
  .slide__do{ margin-top:auto; }
}

/* ---- the field, and the ring around it ----

   The one field a person ever types into sat on the title's descenders, and
   the ring around it was the browser's yellow rather than ours. Both are the
   first thing anybody sees, so both are ours. */
.slide__ask{ margin-top:.55em; }
.slide__ask:focus,
.slide__ask:focus-visible{
  outline:none;
  border-bottom-color:var(--cyan-lift, #35b6ff);
  box-shadow:0 6px 0 -3px var(--cyan-lift, #35b6ff), 0 0 34px -6px var(--cyan-lift, #35b6ff);
}
@media (prefers-reduced-motion:reduce){ .slide__ask{ transition:none } }

/* The last row of a list was finishing flush against the dots. A list needs a
   little air under it or the twelfth thing looks like an accident. */
.rows,.slide__rows{ padding-bottom:6px; }
.slide:has(.rows) .dots,
.dots{ padding-top:2px; }

/* =====================================================================
   The app body. JudeOffice, JudeBox and X-X Judio mount a working surface
   inside the slide. It takes whatever room the words leave, and like every
   other part of a slide it does not scroll: the runtime pages its own
   content instead. When there is a body, the words hold to the top -- a
   page of a document read from the middle of the screen is worse than one
   read from the top, exactly as with a list.
   ===================================================================== */
.slide:has(.slide__body){ justify-content:flex-start; }
.slide__body{
  flex:1 1 auto;min-height:0;width:100%;
  margin-top:14px;
  display:flex;flex-direction:column;
  overflow:hidden;                        /* the law */
}
.slide__body > *{min-height:0;max-width:100%}
.slide[data-kind="hero"] .slide__body,
.slide[data-kind="deep"] .slide__body{color:inherit}
