:root {
  --bg:#FAF9F5; --bg-2:#F0EEE6; --card:#FFFFFF;
  --ink:#1F1E1B; --ink-2:#57544E; --muted:#6F6A62;
  --hairline:rgba(31,30,27,.10); --hairline-strong:rgba(31,30,27,.20);
  --link:#1F1E1B; --focus:#1F1E1B;
  /* Form fields and scrollbars. Ink at low alpha rather than new colours: they stay inside the
     hueless budget (D11) and move with the polarity for free. */
  --field-hover:rgba(31,30,27,.38); --ring:rgba(31,30,27,.14);
  --scroll:rgba(31,30,27,.24); --scroll-hover:rgba(31,30,27,.42);
  /* Without this the browser draws every native control — scrollbars, the number spinner, a
     select's dropdown, autofill — as if the page were light, whatever the page actually is. That
     was the white email box and the 2005 scrollbars on the dark theme. */
  color-scheme:light;
  --font-display:Georgia,"Times New Roman",serif;
  --font-body:"Segoe UI",system-ui,sans-serif;
  --font-mono:"Cascadia Mono",ui-monospace,Consolas,monospace;
  --measure:56.25rem; --container:75rem; --radius:8px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg:#141311; --bg-2:#1D1B18; --card:#1A1815;
    --ink:#FAF9F5; --ink-2:#C9C4BB; --muted:#A39E95;
    --hairline:rgba(250,249,245,.10); --hairline-strong:rgba(250,249,245,.20);
    --link:#FAF9F5; --focus:#FAF9F5;
    --field-hover:rgba(250,249,245,.36); --ring:rgba(250,249,245,.16);
    --scroll:rgba(250,249,245,.22); --scroll-hover:rgba(250,249,245,.40);
    color-scheme:dark;
  }
}
* { margin:0; padding:0; box-sizing:border-box; }
body { background:var(--bg); color:var(--ink); font:400 17px/1.5 var(--font-body); -webkit-font-smoothing:antialiased; }
a { color:var(--link); text-decoration:underline; text-underline-offset:2px; text-decoration-thickness:1px; }
/* Colour no longer marks a link apart, so the underline has to answer the pointer itself. */
a:hover { text-decoration-thickness:2px; }
a:focus-visible, button:focus-visible { outline:2px solid var(--focus); outline-offset:2px; }
code { font-family:var(--font-mono); font-size:.92em; background:var(--bg-2); padding:.08em .35em; border-radius:4px; }

/* ---- Form fields: one look, on every page ------------------------------------------------------
   In CORE, not the tool stylesheet. It used to live in tool.css, and /aisle/ loads only styles.css,
   so the app's fields got no styling at all and fell back to the browser's white box.
   :where() gives these zero specificity on purpose: they are the floor every field stands on, and
   any page-specific rule (.textin, .controls select) still wins without a fight. */
:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color],[type=button],[type=submit],[type=reset],[type=image]), textarea, select) {
  font:inherit; font-size:.95rem; line-height:1.45; color:var(--ink);
  background-color:var(--card); border:1px solid var(--hairline-strong); border-radius:6px;
  padding:.55rem .75rem; min-height:2.5rem;
  transition:border-color .15s ease, box-shadow .15s ease;
}
:where(input, textarea, select):hover:not(:disabled):not(:focus) { border-color:var(--field-hover); }
/* :focus, not :focus-visible. A text field shows where you are typing whether you arrived by mouse
   or keyboard. The border goes to full ink, which carries the contrast; the soft ring only adds
   presence, so the indicator never depends on a low-alpha colour alone. */
:where(input:not([type=checkbox],[type=radio],[type=range],[type=file]), textarea, select):focus {
  outline:none; border-color:var(--ink); box-shadow:0 0 0 3px var(--ring);
}
:where(input, textarea)::placeholder { color:var(--muted); opacity:1; }
:where(input, textarea, select):disabled { opacity:.5; cursor:not-allowed; }
:where(textarea) { resize:vertical; }
/* A chevron drawn from two gradient triangles in --muted, so it follows the polarity. An SVG data
   URI cannot read a custom property and would be one colour on both themes. background-COLOR in
   the base rule, never the shorthand, or this image is wiped. */
:where(select) {
  appearance:none; -webkit-appearance:none; padding-right:2.1rem; cursor:pointer;
  background-image:linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position:calc(100% - 1.15rem) 50%, calc(100% - .8rem) 50%;
  background-size:.36rem .36rem; background-repeat:no-repeat;
}
/* Chrome paints autofilled fields its own pale yellow or blue, which on the dark theme is a
   glaring slab. An inset shadow is the only paint it lets a page put over that. */
:where(input):-webkit-autofill { -webkit-text-fill-color:var(--ink); caret-color:var(--ink); box-shadow:0 0 0 100px var(--card) inset; }
:where(input):-webkit-autofill:focus { box-shadow:0 0 0 100px var(--card) inset, 0 0 0 3px var(--ring); }
:where(input[type=checkbox], input[type=radio], input[type=range]) { accent-color:var(--ink); }

/* ---- Scrollbars ---------------------------------------------------------------------------------
   A thin pill that sits inside a transparent track, the same on the page and inside every box that
   scrolls. Two mechanisms because the engines split: Firefox has only the standard properties, and
   Chromium and Safari have the pseudo-elements — which Chromium IGNORES the moment scrollbar-color
   is set anywhere. So the standard ones apply only where the pseudo-elements do not exist. */
@supports not selector(::-webkit-scrollbar) {
  html { scrollbar-color:var(--scroll) transparent; scrollbar-width:thin; }
}
::-webkit-scrollbar { width:12px; height:12px; }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background:transparent; }
/* The 3px transparent border with content-box clipping is what turns a 12px slab into a 6px pill
   with breathing room — a bigger target to grab than it looks. */
::-webkit-scrollbar-thumb { background-color:var(--scroll); border:3px solid transparent; border-radius:999px; background-clip:content-box; }
::-webkit-scrollbar-thumb:hover { background-color:var(--scroll-hover); }
::-webkit-scrollbar-thumb:active { background-color:var(--ink-2); }

/* The large text area the text tools and the chat use. Moved here from tool.css with the rest of the
   field rules; its focus now comes from the shared rule above rather than a separate hard outline. */
.textin { display:block; width:100%; max-width:var(--measure); margin:1.6rem 0 1.2rem; padding:1rem 1.1rem;
  font:inherit; line-height:1.6; border-radius:var(--radius); }
.wrap { max-width:var(--container); margin:0 auto; padding:0 clamp(1.25rem,4vw,4rem); }
.measure { max-width:var(--measure); }
.vh { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }
.skip { position:absolute; left:-9999px; top:0; background:var(--card); color:var(--ink); padding:.6rem 1rem; z-index:10; }
.skip:focus { left:1rem; top:1rem; }

/* padding-BLOCK, never the shorthand. The header is <header class="site wrap">, so both rules land
   on the same element and a padding:1.4rem 0 shorthand here silently wipes the inline inset .wrap
   sets — which is what pushed the mark and the nav out to the window edges, out of line with the
   content below them. Setting only the block axis leaves that inline padding intact, so the
   header's outer edges line up with the page. */
header.site { display:flex; align-items:center; padding-block:1.4rem; }
header.site .mark { width:28px; height:28px; display:block; }
header.site a.home { display:inline-flex; align-items:center; gap:.65rem; font-family:var(--font-display); font-size:1.05rem; color:var(--ink); text-decoration:none; letter-spacing:.02em; }
header.site a.home:hover span { text-decoration:underline; }
header.site nav { margin-left:auto; font-size:.9rem; }
header.site nav [aria-current="page"] { color:var(--muted); text-decoration:none; }

h1 { font-family:var(--font-display); font-weight:400; line-height:1.1; letter-spacing:-.01em; font-size:clamp(2rem,4.5vw,3.4rem); text-wrap:balance; }
h2 { font-family:var(--font-display); font-weight:400; font-size:clamp(1.4rem,2.4vw,2rem); line-height:1.15; margin:2.6rem 0 .9rem; }
h3 { font-size:1.05rem; font-weight:600; margin:1.8rem 0 .5rem; }
p { margin:.85rem 0; color:var(--ink-2); max-width:var(--measure); }
ul { margin:.85rem 0 .85rem 1.2rem; color:var(--ink-2); }
li { margin:.35rem 0; }
strong { color:var(--ink); }

/* The hero wordmark BECOMES the mark. "Abstract Objective" collapses to its two initials, the A and
   the O close on each other, and at the instant they eclipse the mark takes their place — the ring
   IS the O, and the A already lives inside it. The wordmark and the mark share one centred box so
   the swap happens on the spot with no layout shift.
   Pure CSS on purpose: invariant I1 forbids a script on a content page and the build gate enforces
   it, so the whole sequence is keyframes on a shared clock. Timeline:
     0.08s  the name arrives
     1.70s  ONE 1.15s movement: the words collapse and the initials eclipse, on one curve
     2.78s  the letters hand over to the mark   3.55s  the tagline */
.hero { padding:clamp(4rem,16vh,10rem) 0 clamp(3rem,10vh,6rem); text-align:center; display:flex; flex-direction:column; align-items:center;
  --hero-fs:clamp(2.1rem,7.6vw,6.8rem);
  --mark-size:clamp(64px,9vw,104px);
  /* The size the mark must be BORN at so its ring lands on the O it replaces. Measured: Georgia's
     cap O carries 0.680-0.693em of ink depending on size, and the mark's ring spans 98.5% of its
     box, so 0.686 / 0.985 = 0.697em. Being font-derived it tracks the wordmark at every viewport,
     which is what lets the handoff be seamless without a single breakpoint. */
  --mark-seed:calc(var(--hero-fs) * .697); }
/* A DEFINITE square, not a shrink-to-fit box. The lockup is a flex item, so left to itself it would
   size to the wordmark and shrink as the words collapse — and the mark, absolutely positioned and
   wider than that shrinking box, would stop being centred by margin:auto and jam against its left
   edge. Fixing the box to the mark's own size keeps one true centre for the whole sequence; the
   full name is simply allowed to overflow it left and right until it collapses inside. */
.hero .lockup { position:relative; display:flex; align-items:center; justify-content:center;
  width:var(--mark-size); height:max(var(--mark-size), calc(var(--hero-fs) * 1.25)); }
/* The mark is born at the O's exact size and grows into place, so nothing snaps at the swap.
   Its SIZE is animated rather than a transform: a scale factor would have to know the ratio between
   a font-relative seed and a viewport-relative final size, which CSS cannot divide, and every
   workaround is a pile of breakpoints that are wrong between them. */
.hero .mark { position:absolute; inset:0; margin:auto; width:var(--mark-size); height:var(--mark-size);
  opacity:0; animation:resolve .64s cubic-bezier(.2,.7,.25,1) 2.8s forwards; }
.hero h1 { white-space:nowrap; font-size:var(--hero-fs); opacity:0; transform:translateY(12px);
  animation:enter .82s cubic-bezier(.2,.6,.2,1) .08s forwards, dim .32s ease 2.78s forwards; }
/* Georgia sets a cap glyph's ink centre within a rounding error of its box centre, so the default
   origin is already the optical centre — do not "correct" it. */
.hero h1 span { display:inline-block; vertical-align:bottom; }
/* The collapse and the eclipse are ONE movement, so they share ONE clock and ONE curve: same delay,
   same duration, same easing on all three. The letters travel from the full name to eclipsed along
   a single ease, which is the only way the sum of two animations is smooth — overlapping them with
   different curves still jolts, because whichever ends first drops its share of the velocity in one
   frame. Measured at 20ms: the earlier two-curve version peaked at 38px/frame and crashed to 3. */
.hero .rest { overflow:hidden; white-space:pre; animation:collapse 1.15s cubic-bezier(.42,0,.58,1) 1.7s forwards; }
/* Measured widths, not a round ch guess. "bstract " is 3.212em of Georgia and "bjective" 3.328em,
   while the old 8.2ch was 5.03em — so 36% of the collapse was spent shrinking a max-width still
   wider than the text, moving nothing. That dead travel was the hesitation before the words seemed
   to go anywhere. ~15% headroom remains, because overflow:hidden would clip the words outright on a
   face wider than Georgia (DejaVu Serif, where there is no Georgia to fall back from), and a slice
   of dead travel this small is spent inside the ease-in where the curve is near-stationary anyway. */
.hero .r1 { max-width:3.7em; } .hero .r2 { max-width:3.85em; }
.hero .gA { animation:eclipse-a 1.15s cubic-bezier(.42,0,.58,1) 1.7s forwards; }
.hero .gO { animation:eclipse-o 1.15s cubic-bezier(.42,0,.58,1) 1.7s forwards; }
.hero .tag { margin-top:1.6rem; color:var(--muted); font-size:clamp(.95rem,1.6vw,1.1rem); opacity:0; animation:fade .9s ease 3.55s forwards; }
@keyframes enter { to { opacity:1; transform:translateY(0); } }
@keyframes fade { to { opacity:1; } }
@keyframes dim { to { opacity:0; } }
@keyframes collapse { to { max-width:0; opacity:0; } }
@keyframes resolve {
  from { opacity:0; width:var(--mark-seed); height:var(--mark-seed); }
  to   { opacity:1; width:var(--mark-size); height:var(--mark-size); }
}
/* Pure translation. Neither letter is resized: Georgia's cap A already carries WIDER ink than its
   cap O (0.734em against 0.693em), so the two simply sliding together give an A that fills the ring
   and breaks its top — which is the mark. Growing the O was solving a problem that did not exist.
   The two shifts are UNEQUAL and measured. The O's advance is wider than the A's, so the pair's
   centre sits nearer the O and each glyph needs its own distance to reach it. Move both the same and
   the eclipse lands off the mark's centre. Being em-based, these hold at every clamped size. */
@keyframes eclipse-a { to { transform:translateX(.367em); } }
@keyframes eclipse-o { to { transform:translateX(-.330em); } }
@media (prefers-reduced-motion: reduce) {
  /* No transformation to watch, so show the finished lockup: the mark above the full name. */
  .hero .lockup { flex-direction:column; gap:2rem; width:auto; height:auto; }
  .hero .mark { position:static; margin:0; }
  .hero .mark, .hero h1, .hero .rest, .hero .gA, .hero .gO, .hero .tag { animation:none; opacity:1; transform:none; }
}

.band { border-top:1px solid var(--hairline); padding:clamp(3.5rem,9vh,7.5rem) 0; }
.eyebrow { font-family:var(--font-mono); font-size:.72rem; font-weight:500; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin-bottom:1.1rem; }
.band h2 { margin:0 0 .9rem; font-size:clamp(1.7rem,3.2vw,2.5rem); line-height:1.15; }
.band p.lead { font-size:1.08rem; margin:.4rem 0 0; }
.band .more { display:inline-block; margin-top:1.7rem; }
.quiet { color:var(--muted); margin-top:1.5rem; font-size:.95rem; }

.cards { display:grid; gap:1rem; margin:2.4rem 0 5rem; }
.cards.cols2 { grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); margin:2.2rem 0 0; }
a.card { display:block; background:var(--card); border:1px solid var(--hairline); border-radius:var(--radius); padding:1.6rem 1.7rem; text-decoration:none; color:var(--ink); transition:border-color .2s; }
a.card:hover { border-color:var(--hairline-strong); }
a.card:hover .ev { text-decoration-thickness:2px; }
a.card .meta { font-family:var(--font-mono); font-size:.72rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
a.card h2 { margin:.5rem 0 .6rem; font-size:1.45rem; }
a.card p.claim { margin:0; color:var(--ink-2); }
a.card .ev { display:inline-block; margin-top:.9rem; color:var(--link); text-decoration:underline; text-underline-offset:2px; font-size:.85rem; }
.ticon { width:46px; height:46px; color:var(--ink); display:block; }
.card .ticon { width:42px; height:42px; margin-bottom:.75rem; }
/* A shelf with one thing on it: auto-fit would stretch that single card the full 75rem, which reads
   as a banner rather than as one of several. Cap it and it still reads as a card. */
.cards.cols2 > .card:only-child { max-width:38rem; }

/* The AIsle's mark. Its viewBox is 1000x745, so only the width is set and the height follows —
   fixing both would letterbox it inside its own box at every size. */
.aislemark { display:block; width:clamp(88px,12vw,136px); height:auto; color:var(--ink); margin:2.6rem 0 1.6rem; }
article header.entry .aislemark { margin:0 0 1.3rem; }   /* header.entry already pays the top space */
/* The connected accounts, as chips. They are a list of names, not prose, and reading them as prose
   makes five vendors look like a sentence you have to parse. */
.connectors { list-style:none; margin:1.2rem 0 .6rem; padding:0; display:flex; flex-wrap:wrap; gap:.55rem; max-width:var(--measure); }
.connectors li { margin:0; font-family:var(--font-mono); font-size:.8rem; color:var(--ink); background:var(--card); border:1px solid var(--hairline-strong); border-radius:999px; padding:.3rem .85rem; }

/* The AIsle app shell. This is the one page on the site that is not a document — press AIsle and
   you are in a product, not reading about one. It keeps the AO mark on the left so you are never
   lost inside it, and puts its own name beside it: an app inside AO, which is exactly what it is.
   The rest of the site's furniture (band, card, measure) is deliberately absent. */
/* flex-wrap so a narrow bar stacks instead of crushing. Without it the only item that can shrink is
   the one with wrapping text, so "What this is" collapsed to its longest word and grew to three
   lines while everything beside it stayed one — which is what made the bar look misaligned rather
   than merely cramped. */
header.site.appbar { gap:.7rem; row-gap:.5rem; flex-wrap:wrap; border-bottom:1px solid var(--hairline); }
header.site.appbar nav a { white-space:nowrap; }
header.site.appbar .appsep { color:var(--muted); font-size:1.1rem; }
header.site.appbar .appname { display:inline-flex; align-items:center; gap:.5rem; margin:0; font-size:1.05rem; letter-spacing:.02em; }
/* Matched on HEIGHT, not width. The AIsle mark is 1.34x wider than it is tall, so setting both to
   the AO mark's 28px made it 19px high and it read as the smaller of the two. */
header.site.appbar .appname .aislemark { height:28px; width:auto; margin:0; }
main.app { padding:2rem 0 4rem; min-height:60svh; }
main.app h2 { font-size:1.2rem; margin:0 0 .5rem; }
/* Scoped to the panels, not to main.app: "main.app p" outweighs ".notopen p" and ".appfoot"
   (element+class+element beats class+element), which quietly squeezed both of those to 46ch. */
.panel p { max-width:46ch; }

/* Said before anything else on the screen, because the screen below it is drawn as if it worked. */
.notopen { display:flex; align-items:baseline; gap:.9rem 1.2rem; flex-wrap:wrap; margin:0 0 2rem; padding:1rem 1.3rem; border:1px solid var(--hairline-strong); border-radius:var(--radius); background:var(--bg-2); }
.notopen p { margin:0; font-size:.95rem; max-width:none; }

/* The messaging-app shape: a rail of lists on the left, the open conversation on the right. The
   rail holds two panels because in this product they are the same question asked twice — which
   room, and who is in it. Below 60rem there is no room for two columns and it stacks. */
.appgrid { display:grid; grid-template-columns:minmax(0,21rem) minmax(0,1fr); gap:1rem; }
.rail { display:grid; gap:1rem; align-content:start; }
/* One column cannot show the room list and the room at once without burying one of them — the
   conversation ended up below a five-row assistants panel and looked like it had vanished. So the
   narrow layout shows whichever you are in, with a back arrow to the other, which is what every
   messaging app on a phone does. Wide stays side by side and the arrow is not there. */
button.backbtn { display:none; font:inherit; font-size:.85rem; line-height:1.2; color:var(--ink-2); background:none; border:1px solid var(--hairline-strong); border-radius:6px; padding:.32rem .7rem; cursor:pointer; margin-right:.2rem; white-space:nowrap; }
button.backbtn:hover { border-color:var(--ink-2); color:var(--ink); }
@media (max-width:60rem) {
  .appgrid { grid-template-columns:1fr; }
  .appgrid.chatopen .rail { display:none; }
  .appgrid:not(.chatopen) .chatpanel { display:none; }
  .appgrid.chatopen button.backbtn { display:inline-flex; }
  .msglog { max-height:none; }
}
.panel { background:var(--card); border:1px solid var(--hairline); border-radius:var(--radius); padding:1.5rem 1.6rem; }
.panelhead { display:flex; align-items:center; justify-content:space-between; gap:1rem; }
.panelhead h2 { margin:0; }

/* One row list, two jobs: the chats and the assistants. They are the same object — a named thing
   with one control beside it — so they get the same furniture rather than two lookalike rules. */
.rowlist { list-style:none; margin:1.1rem 0 0; padding:0; display:grid; gap:1px; background:var(--hairline); border:1px solid var(--hairline); border-radius:var(--radius); overflow:hidden; }
.rowlist li { margin:0; display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:.7rem 1rem; background:var(--card); }
.rowlist .cname { color:var(--ink); font-weight:500; }
/* li.rowempty, not .rowempty: ".rowlist li" is class+element and would outweigh a bare class. */
.rowlist li.rowempty { display:block; color:var(--muted); font-size:.9rem; padding:1rem; }
/* Inert on purpose, and visibly so. An app that cannot do anything yet, drawn with buttons that
   look live, is a lie told in CSS — so the disabled state is the designed state, not an oversight. */
/* cursor follows the DISABLED state, not the class. These buttons start disabled and the island
   enables them once you are signed in; a permanent not-allowed cursor on a working button is the
   interface telling you it is broken while it quietly works. */
.rowlist button, button.newchat, button.signin { font:inherit; font-size:.85rem; color:var(--ink-2); background:none; border:1px solid var(--hairline-strong); border-radius:6px; padding:.3rem .85rem; cursor:pointer; white-space:nowrap; }
.rowlist button:hover:enabled, button.newchat:hover:enabled { border-color:var(--ink-2); color:var(--ink); }
button:disabled { cursor:not-allowed; color:var(--muted); }
header.site.appbar nav { display:flex; align-items:center; gap:1rem; }
a.signin { font-size:.85rem; color:var(--ink-2); border:1px solid var(--hairline-strong); border-radius:6px; padding:.32rem .85rem; text-decoration:none; white-space:nowrap; }
a.signin:hover { border-color:var(--ink-2); color:var(--ink); }

/* The account, rightmost, which is where every app puts it and where people look for sign-out.
   A <details> so the menu opens without a line of JavaScript deciding when it should. */
#account { display:flex; align-items:center; }
details.account { position:relative; display:flex; align-items:center; }
details.account summary { list-style:none; cursor:pointer; display:flex; align-items:center; }
details.account summary::-webkit-details-marker { display:none; }
/* 999px rather than 50%: a percentage radius is relative to the box, so anything that makes the box
   non-square — a stray padding, a border-box surprise — quietly turns the circle into a lozenge.
   flex:none so it is never the item that gets squashed when the bar runs out of room. */
.avatar { flex:none; display:grid; place-items:center; width:30px; height:30px; border-radius:999px; background:var(--bg-2); border:1px solid var(--hairline-strong); color:var(--ink); font-size:.85rem; font-weight:600; line-height:1; }
details.account[open] .avatar { border-color:var(--ink-2); }
.accountmenu { position:absolute; right:0; top:calc(100% + .5rem); z-index:20; min-width:15rem; padding:.9rem 1rem; background:var(--card); border:1px solid var(--hairline-strong); border-radius:var(--radius); box-shadow:0 8px 24px rgba(0,0,0,.18); }
.accountmenu p { margin:0; max-width:none; }
.accountwho { color:var(--ink); font-weight:500; }
.accountmail { font-size:.85rem; color:var(--muted); overflow-wrap:anywhere; }
.accountmenu button { margin-top:.8rem; }

/* the conversation */
button.chatrow { display:block; width:100%; text-align:left; font:inherit; color:var(--ink); background:none; border:none; padding:.7rem 1rem; cursor:pointer; }
button.chatrow:hover { background:var(--bg-2); }
button.chatrow.on { background:var(--bg-2); font-weight:500; }
.rowlist li:has(> button.chatrow) { padding:0; display:block; }
/* The conversation column is narrow on purpose. Prose is read in a line of roughly 60 to 70
   characters and this is prose — assistants write paragraphs, not table rows. The panel can be as
   wide as the screen allows; the reading column stays where the eye can track it. */
.msglog { flex:1; margin:1rem 0; padding:1rem 0; border-top:1px dashed var(--hairline-strong); display:grid; gap:1.4rem; align-content:start; overflow-y:auto; max-height:30rem; }
.msg { display:grid; gap:.25rem; max-width:64ch; }
.msg .msgwho { font-family:var(--font-mono); font-size:.7rem; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.msg.agent .msgwho { color:var(--ink-2); }
.msgbody > :first-child { margin-top:0; }
.msgbody > :last-child { margin-bottom:0; }
.msgbody p { margin:.5rem 0; color:var(--ink); overflow-wrap:anywhere; max-width:none; }
.msgbody ul { margin:.5rem 0 .5rem 1.2rem; color:var(--ink); }
.msgbody h2, .msgbody h3 { font-size:1rem; margin:.9rem 0 .3rem; font-family:var(--font-body); font-weight:600; }
/* Code from an assistant is often wide. It scrolls inside its own box rather than widening the room. */
.msgbody pre { margin:.6rem 0; padding:.7rem .85rem; background:var(--bg-2); border:1px solid var(--hairline); border-radius:6px; overflow-x:auto; font-family:var(--font-mono); font-size:.78rem; line-height:1.5; }
.msgbody pre code { background:none; padding:0; font-size:inherit; }
textarea.say { margin:0 0 .6rem; max-width:64ch; }

/* Who is in a room, and what they brought, at a glance. Overlapping circles the way a repository
   host shows contributors, each with its assistant clipped to the corner. */
.faces { display:inline-flex; align-items:center; margin-left:auto; padding-left:.6rem; }
.face { position:relative; width:22px; height:22px; margin-left:-6px; }
.face:first-child { margin-left:0; }
.facein { display:grid; place-items:center; width:22px; height:22px; border-radius:999px; background:var(--bg-2); border:1px solid var(--hairline-strong); box-shadow:0 0 0 2px var(--card); color:var(--ink); font-size:.62rem; font-weight:600; line-height:1; }
/* A FIXED white plate, not a theme token. Brand marks are drawn for light grounds — that is what
   an app icon is — so the badge carries its own plate in both polarities rather than following the
   page. Using --bg-2 here meant the ground went near-black in dark mode, which is what made Claude
   orange muddy and would have made the black Cursor mark invisible outright. The ring stays the
   card colour, so the plate still separates from the circle it is clipped to either way round. */
.agentdot { position:absolute; right:-4px; bottom:-4px; display:grid; place-items:center; width:14px; height:14px; border-radius:999px; background:#FFFFFF; color:#111111; font-size:.5rem; font-weight:700; line-height:1; box-shadow:0 0 0 1.5px var(--card); }
.facemore { margin-left:.35rem; font-size:.7rem; color:var(--muted); }
button.chatrow { display:flex; align-items:center; gap:.5rem; }
.chattitle { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* The key, once. The paste-to-your-assistant route comes first because most people have never
   opened a terminal and all of them have an assistant sitting right there. */
/* The copy control sits ON the block, top right, which is where everyone has copied from for years.
   The pre gets right padding so a long line scrolls under the button rather than behind it. */
.codeblock { position:relative; margin:.5rem 0; }
/* Wraps rather than scrolls. The copy button copies from the stored string, never from the DOM, so
   wrapping is purely visual and costs nothing — and it removes the horizontal scrollbar that was
   appearing under every block, plus the second one the page grew underneath it. */
.codeblock pre { margin:0; padding:.8rem 3.2rem .8rem .9rem; background:var(--card); border:1px solid var(--hairline); border-radius:6px; font-family:var(--font-mono); font-size:.76rem; line-height:1.55; color:var(--ink); white-space:pre-wrap; overflow-wrap:anywhere; }
button.copybtn { position:absolute; top:.45rem; right:.45rem; display:grid; place-items:center; width:30px; height:30px; padding:0; color:var(--muted); background:var(--bg-2); border:1px solid var(--hairline-strong); border-radius:6px; cursor:pointer; }
button.copybtn svg { width:15px; height:15px; }
button.copybtn:hover { color:var(--ink); border-color:var(--ink-2); }
button.copybtn.done { color:var(--ink); border-color:var(--ink-2); }

.keyway { margin:1.1rem 0 .2rem; font-family:var(--font-mono); font-size:.7rem; letter-spacing:.08em; text-transform:uppercase; color:var(--ink); }
.keynote { margin:0; font-size:.88rem; color:var(--muted); }
.keybox details { margin-top:.9rem; border-top:1px solid var(--hairline); padding-top:.7rem; }
.keybox summary { cursor:pointer; font-size:.88rem; color:var(--ink-2); }
.agentchip { color:var(--ink-2); }
.agentchip.pending { color:var(--muted); font-style:italic; }
button.linkbtn { font:inherit; font-size:.8rem; color:var(--muted); background:none; border:none; padding:0 0 0 .35rem; cursor:pointer; text-decoration:underline; text-underline-offset:2px; }
button.linkbtn:hover { color:var(--ink); }
/* Marks, at the two sizes that matter. Twelve pixels of shape beats twelve pixels of colour, so
   the badge is monochrome; the picker has room for each mark to be itself. */
/* 9px, not 10. The largest square that fits inside a 14px circle is 14/sqrt(2) = 9.9, and the
   Claude mark is square — at 10px its corners sat exactly on the curve, which is what made a
   perfectly round plate read as an egg. 9px leaves the margin a circle needs and a square does not. */
.agentdot svg { width:9px; height:9px; display:block; }
/* Same reasoning as the badge: a mark gets its own white plate wherever it appears, so a
   monochrome one is never near-white on white and a coloured one never goes muddy on near-black. */
.brandmark { display:grid; place-items:center; width:22px; height:22px; border-radius:6px; background:#FFFFFF; box-shadow:0 0 0 1px var(--hairline-strong); }
.brandmark svg { width:18px; height:18px; display:block; }
.clientpick { display:flex; flex-wrap:wrap; gap:.5rem; margin:.9rem 0 .2rem; }
button.clientopt { display:flex; align-items:center; gap:.45rem; font:inherit; font-size:.82rem; color:var(--ink-2); background:var(--card); border:1px solid var(--hairline-strong); border-radius:999px; padding:.3rem .8rem .3rem .45rem; cursor:pointer; }
button.clientopt:hover { border-color:var(--ink-2); color:var(--ink); }
button.clientopt.on { border-color:var(--ink); color:var(--ink); background:var(--bg-2); }
button.ghostbtn { font:inherit; font-size:.85rem; color:var(--ink-2); background:none; border:1px solid var(--hairline-strong); border-radius:6px; padding:.32rem .85rem; cursor:pointer; }
button.ghostbtn:hover { border-color:var(--ink-2); color:var(--ink); }

/* The key, shown once. Deliberately loud, because it cannot be shown again. */
.keybox { margin:0 0 1.4rem; padding:1.1rem 1.2rem; background:var(--bg-2); border:1px solid var(--ink-2); border-radius:var(--radius); }
.keybox h3 { margin:0 0 .3rem; }
.keybox pre { margin:.7rem 0; padding:.8rem .9rem; background:var(--card); border:1px solid var(--hairline); border-radius:6px; font-family:var(--font-mono); font-size:.76rem; line-height:1.5; color:var(--ink); white-space:pre-wrap; overflow-wrap:anywhere; }
.keybox p { max-width:none; }
/* The address and its button share one line and one height, so the button reads as belonging to
   the field rather than floating under it. Wraps on a narrow screen instead of squeezing the field. */
.inviterow { display:flex; flex-wrap:wrap; gap:.5rem; align-items:stretch; margin:.5rem 0 .2rem; }
.inviterow .textin { flex:1 1 14rem; min-width:0; margin:0; }
.invitebox .keynote:not([hidden]) { margin:.4rem 0 .2rem; color:var(--ink); }
.inviterow :where(input) { flex:1 1 14rem; min-width:0; }

/* ---- A popup ------------------------------------------------------------------------------------
   A native <dialog> opened with showModal(). The browser supplies what a hand-built overlay gets
   wrong: the top layer (nothing on the page can sit above it), focus kept inside while it is open,
   Escape to close, and everything behind it made inert to clicks and screen readers. */
dialog.modal {
  margin:auto; padding:0; width:min(34rem, calc(100vw - 2rem)); max-height:calc(100dvh - 2rem);
  color:var(--ink); background:var(--bg); border:1px solid var(--hairline-strong); border-radius:12px;
  box-shadow:0 28px 70px -18px rgba(0,0,0,.5); overflow:auto; overscroll-behavior:contain;
}
dialog.modal::backdrop { background:rgba(12,11,10,.55); backdrop-filter:blur(3px); }
dialog.modal[open] { animation:modal-in .18s ease-out; }
dialog.modal[open]::backdrop { animation:backdrop-in .18s ease-out; }
@keyframes modal-in { from { opacity:0; transform:translateY(8px) scale(.985); } }
@keyframes backdrop-in { from { opacity:0; } }
@media (prefers-reduced-motion: reduce) { dialog.modal[open], dialog.modal[open]::backdrop { animation:none; } }
.modalhead { display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:1.1rem 1rem .3rem 1.4rem; }
.modalhead h3 { margin:0; font-size:1.15rem; }
button.modalx { display:grid; place-items:center; width:34px; height:34px; font:inherit; font-size:1.3rem; line-height:1; color:var(--muted); background:none; border:1px solid transparent; border-radius:8px; cursor:pointer; }
button.modalx:hover { color:var(--ink); border-color:var(--hairline-strong); }
.modalbody { padding:.1rem 1.4rem 1.4rem; }
.modalbody p { max-width:none; }
.modalbody .keyway:first-child { margin-top:.6rem; }
.modalsep { border:0; border-top:1px solid var(--hairline); margin:1.3rem 0 0; }
.rowlist .later { font-family:var(--font-mono); font-size:.7rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
.freeline { margin-top:1.3rem; font-size:.95rem; }

/* The path from nothing to working, numbered. It is the first thing a signed-out visitor should
   read, so it sits where the open conversation will be rather than under a heading further down. */
.flowlist { margin:.4rem 0 1rem 1.3rem; padding:0; color:var(--ink-2); }
.flowlist li { margin:.5rem 0; padding-left:.2rem; }
.connectors li.on { border-color:var(--ink); color:var(--ink); }

.chatpanel { min-height:22rem; display:flex; flex-direction:column; }
.chatempty { flex:1; display:flex; flex-direction:column; gap:.3rem; padding:1.5rem 0 0; border-top:1px dashed var(--hairline-strong); margin-top:1rem; }
.chatempty p { color:var(--ink-2); }
.chatempty .quiet { margin-top:0; font-size:.9rem; }
.appfoot { margin-top:2.4rem; font-size:.9rem; color:var(--muted); max-width:none; }

/* How an assistant joins. Eight clients, each with a block of syntax, is a wall of code if it is all
   open at once — so each one is a <details>. Native, no script, and the page still works with
   JavaScript off, which invariant I1 requires of everything under /aisle/. */
.joinpanel { margin-top:3rem; padding-top:2rem; border-top:1px solid var(--hairline); }
.joinpanel h2 { margin:0 0 .7rem; font-size:1.3rem; }
/* A URL inside <code> has no break opportunities, so the cell refused to narrow and pushed the
   whole page sideways on a phone. Let it break anywhere, and pin the table to the column so it
   cannot be widened by its contents in the first place. */
.joinpanel table.plain { width:100%; table-layout:fixed; }
.joinpanel table.plain th { width:9rem; }
.joinpanel table.plain code { overflow-wrap:anywhere; }
.joins { display:grid; gap:1px; background:var(--hairline); border:1px solid var(--hairline); border-radius:var(--radius); overflow:hidden; margin-top:1.6rem; max-width:var(--measure); }
.joins details { background:var(--card); }
.joins summary { cursor:pointer; padding:.8rem 1rem; display:flex; align-items:baseline; gap:.7rem; flex-wrap:wrap; }
.joins summary::marker { color:var(--muted); }
.joins .jclient { color:var(--ink); font-weight:500; }
.joins .jeffort { font-family:var(--font-mono); font-size:.72rem; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.joins .jwarn { font-family:var(--font-mono); font-size:.66rem; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-2); border:1px solid var(--hairline-strong); border-radius:999px; padding:.1rem .5rem; }
/* Commands are wide. They scroll inside their own box rather than pushing the page sideways. */
.joins pre { margin:0 1rem .6rem; padding:.9rem 1rem; background:var(--bg-2); border-radius:6px; overflow-x:auto; font-family:var(--font-mono); font-size:.78rem; line-height:1.55; color:var(--ink); }
.joins .jnote { margin:0 1rem 1rem; font-size:.88rem; color:var(--muted); max-width:none; }

/* AIsle — the about page. The status badge is not decoration. It is the page saying out loud, before the
   reader invests any attention, how far from usable the thing actually is. */
.statusbox { display:flex; align-items:baseline; gap:.9rem 1.2rem; flex-wrap:wrap; max-width:var(--measure); margin:0 0 2.2rem; padding:1rem 1.3rem; background:var(--bg-2); border:1px solid var(--hairline); border-radius:var(--radius); }
.statusbox p { margin:0; font-size:.95rem; }
.badge { font-family:var(--font-mono); font-size:.68rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase; color:var(--ink); border:1px solid var(--hairline-strong); border-radius:999px; padding:.24rem .7rem; white-space:nowrap; }
/* "What it is" beside "what it is not", because the second column is only honest while the reader
   can see it without scrolling past the first. min(100%,20rem) so the pair stacks instead of
   overflowing when the viewport is narrower than one column. */
.pair { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,20rem),1fr)); gap:.2rem 2.6rem; max-width:var(--measure); margin:2.2rem 0 1rem; }
.pair h3 { margin-top:1.3rem; }

article header.entry { padding-top:3.5rem; }
article header.entry .ticon { margin-bottom:1rem; }
article .meta { font-family:var(--font-mono); font-size:.75rem; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin-bottom:1rem; }
article h1 { margin-bottom:1.4rem; }
article p.lead { font-size:1.08rem; }
.claimbox { font-family:var(--font-display); font-size:1.25rem; line-height:1.5; color:var(--ink); background:var(--bg-2); border-left:3px solid var(--ink); border-radius:0 var(--radius) var(--radius) 0; padding:1.1rem 1.4rem; margin:0 0 2.2rem; max-width:var(--measure); }
section.evidence { margin:3rem 0 4rem; padding:1.6rem 1.7rem; background:var(--card); border:1px solid var(--hairline); border-radius:var(--radius); max-width:var(--measure); }
section.evidence h2 { margin:0 0 1rem; font-size:1.3rem; }
section.evidence .numbers li { color:var(--ink-2); }
section.evidence .sources { margin-top:1.1rem; padding-top:1rem; border-top:1px solid var(--hairline); font-size:.9rem; }

.likebox { display:flex; align-items:center; gap:.9rem; margin:0 0 2.4rem; }
button.like { display:inline-flex; align-items:center; gap:.5rem; font:inherit; font-size:.95rem; color:var(--ink); background:var(--card); border:1px solid var(--hairline-strong); border-radius:var(--radius); padding:.45rem .95rem; cursor:pointer; transition:border-color .2s, background-color .2s; }
button.like:hover { border-color:var(--ink-2); }
.heart { width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.8; }
button.like.liked .heart { fill:#FF0000; stroke:#FF0000; }
.likenote { font-size:.8rem; color:var(--muted); }
.cardwrap { position:relative; }
.cardwrap .card { height:100%; }
.cardlike { position:absolute; top:.85rem; right:.85rem; z-index:1; display:inline-flex; background:none; border:none; padding:.35rem; cursor:pointer; color:var(--muted); border-radius:6px; }
.cardlike:hover { color:var(--ink); }
.cardlike .heart { width:17px; height:17px; }
.cardlike.liked { color:#FF0000; }
.cardlike.liked .heart { fill:#FF0000; stroke:#FF0000; }

table.plain { border-collapse:collapse; margin:1.2rem 0; font-size:.92rem; max-width:var(--measure); }
table.plain th, table.plain td { text-align:left; padding:.5rem .9rem .5rem 0; border-bottom:1px solid var(--hairline); vertical-align:top; }
table.plain th { font-weight:600; color:var(--ink); }
table.plain td { color:var(--ink-2); }

footer.site { border-top:1px solid var(--hairline); margin-top:4rem; padding:1.6rem 0 2.4rem; font-size:.85rem; color:var(--muted); }
footer.site a { color:var(--muted); }
