/* === AIOS Master Production CSS (v4.1) === */
:root {
    --aios-bg:#050505;--aios-surface:#0f0f0f;--aios-border:rgba(46,230,214,0.25);
    --aios-text:#ffffff;--aios-text-muted:#aaa;--aios-accent:#2ee6d6;--aios-accent-deep:#1a9fa1;
    --aios-danger:#ff4b5c;--aios-radius:12px;
    --aios-shadow:0 10px 40px rgba(0,0,0,0.8);
}

*,*::before,*::after{box-sizing:border-box;}
html,body{margin:0;padding:0; background:var(--aios-bg); color:var(--aios-text); font-family:system-ui, sans-serif;}

/* display:grid belongs on the base selector. It used to be declared only inside the dashboard-mode
   rule below, so in every other mode .aios-grid fell back to <main>'s block layout — which
   silently voided the overview column rule, voided the old @media (max-width:768px) override, and
   left overview and execution stacking by accident instead of by rule. Declaring it here is what
   makes the per-mode rules reachable; each of those now differs only in columns and height, which
   is how they already read. */
.aios-grid { display: grid; }

/* --- DASHBOARD MODE: FULLSCREEN LOCK --- */
body[data-mode="dashboard"] {
    height: 100dvh; overflow: hidden; display: flex; flex-direction: column;
}
body[data-mode="dashboard"] .aios-app {
    flex: 1; display: flex; flex-direction: column; height: 100%; padding: 10px 15px;
}
body[data-mode="dashboard"] .aios-grid {
    flex: 1; display: grid; grid-template-columns: 350px 1fr 350px; grid-template-rows: 100%; gap: 15px; overflow: hidden;
}

/* --- ADAPTIVE MODES (OVERVIEW & EXECUTION) --- */
body[data-mode="overview"], body[data-mode="execution"] {
    height: auto;
    overflow-y: auto;
}
body[data-mode="overview"] .aios-grid {
    grid-template-columns: var(--aios-sidebar-width, 350px) 1fr;
    height: auto;
}
body[data-mode="overview"] .aios-col-side:last-child { display: none; }
/* Execution's single column, stated as a mode rule rather than the @media (max-width:768px) it
   replaces. updateViewMode()'s buckets are this repo's only breakpoint vocabulary (decision 12),
   and the media query disagreed with them at exactly 768px — where the JS says overview and the
   query said one column. One source now. */
body[data-mode="execution"] .aios-grid { grid-template-columns: 1fr; height: auto; }

/* --- COLUMN LOGIC --- */
.aios-col-side, .aios-col-main { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
.aios-fill-card { flex: 1 1 auto; min-height: 0; }

/* --- SYSTEM BAR --- */
.aios-osbar { 
    display: flex; justify-content: space-between; align-items: center; padding: 10px 20px; 
    background: #000; border-bottom: 1px solid var(--aios-border);
}
/* min-width:0 is what actually makes the ellipsis below work. A flex item defaults to
   min-width:auto and refuses to shrink below its content, so at 412px .aios-osbar-left
   measured 514px against a 412px bar — WIDER THAN THE VIEWPORT. The overflow did not land on
   the greeting (which was already prepared to truncate); it squeezed the LOG OUT button until
   its label wrapped to two lines, and that is what grew the system bar 66px -> 81px and pushed
   the entire grid down. Measured with sources: that single shift was 0.29 of a 0.296 CLS —
   98% of everything left on the page, from one flex item that could not shrink. */
.aios-osbar-left { display: flex; align-items: center; gap: 20px; min-width: 0; }
/* And the button is the thing that must NOT absorb the squeeze: it is a fixed control with a
   two-word label, so it holds its size and the greeting gives way instead. */
#aios-logout-btn { flex: 0 0 auto; white-space: nowrap; }

/* The greeting ("Hey <name>, Today is <date/time>") is EMPTY in the markup and filled by JS
   on load. At execution width that wraps to THREE lines, growing the system bar 66px -> 86px
   and pushing the entire page down at once — which is why Lighthouse scored the shift against
   `main.aios-grid` itself, the only thing below the bar.

   This was a `min-height: 44px` reservation, and 44px was simply wrong: two lines, measured
   against content that renders three. Reserving the right number is not the fix either,
   because the wrap point depends on how long the name is and how wide the date formats — it
   is a guess per user, and a wrong guess is either dead chrome or a shift.

   So the greeting is held to ONE line instead, and truncated if it does not fit. That is
   deterministic at every width and for every name, it removes the shift completely rather
   than absorbing it, and it hands ~42px of permanent chrome back to the work area, which is
   the point on a phone. `min-width: 0` is required: the greeting is a flex item of
   .aios-osbar-left and would otherwise refuse to shrink below its longest word and wrap
   anyway — the same trap already documented on .aios-collapse-header h3.
   Execution only. Measured empty vs. filled with a long name and a full timestamp, the bar
   holds at 66px at both 1024px and 1440px, so the wider modes keep the full greeting. */
body[data-mode="execution"] .aios-osbar-greeting {
    min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* WHAT GETS DROPPED ON A PHONE IS CHOSEN, NOT LEFT TO THE ELLIPSIS.
   The one-line rule above is settled and stays — wrapping this bar is what cost
   0.29 of a 0.296 CLS. But truncation on its own picks the worst thing to lose:
   the overflow falls off the RIGHT, which is where the clock is, so the part
   that actually changes second to second was the part being cut, and the phone
   was left reading "AIOS: Hey <name>, Today is" — a sentence with its object
   removed. Measured at 375px with the controls now behind one menu button, the
   greeting has 226px and the full sentence needs roughly twice that.
   So the filler words and the parenthetical date go, and the day and the time
   stay. "(8/10/2026)" is a restatement of "Monday August 10th" in digits, which
   makes it the one part that costs width and adds no information.

   Measured at 375px, needed against the 226px available:
     full sentence, long name          676px
     filler and numeric date dropped   380px
     ... with a short name             296px
     ... abbreviated day, no brand     fits
   So the abbreviation is not decoration, it is the difference between the bar
   showing the time and not showing it.

   SUPERSEDED IN PART, 2026-08-10 (owner ruling): the greeting is TWO LINES on a
   phone, not one. Everything above about choosing what to drop still holds —
   only the "one line" half changed, and the rules that implement the stack are
   below. */
/* "TODAY IS" IS BACK ON LINE ONE (owner ruling, 2026-08-16). It was dropped as
   a filler word when this bar was ONE line and every pixel was contested. The
   stack changed that premise: line one holds "Hey <name>," and nothing else,
   with room to spare, so the connector no longer costs anything the user
   wanted — and without it line one reads as a greeting that was cut off.
   Measured at 375px: line one has 246px; "Hey Kevin, Today is" needs 132px and
   a 16-character name needs 214px, so it fits with the longest name tried.
   The NUMERIC date stays dropped: it restates line two in digits, which is the
   one part that costs width and adds nothing. */
body[data-mode="execution"] .aios-osbar-greeting .aios-date-numeric,
body[data-mode="execution"] .aios-osbar-greeting .aios-date-day { display: none; }

/* ---------------------------------------------------------------------------
   TWO LINES ON A PHONE: "Hey <name>," over the date and time (owner ruling)
   ---------------------------------------------------------------------------
   "Hey <name>" is the half of this bar that is FOR the user, and squeezing the
   whole sentence onto one line was costing it — the previous rule shortened the
   name to keep the clock, so the greeting degraded exactly where it mattered.
   Stacked, each half gets the full column and neither has to lose to the other.

   THIS IS NOT THE WRAP THAT COST 0.29 CLS, AND THE DIFFERENCE IS THE WHOLE
   REASON IT IS SAFE. That failure was an UNPREDICTABLE wrap: the line count
   depended on the name's length and the date's format, so the bar was 66px or
   86px depending on who logged in, and it changed after the first paint when JS
   filled the greeting in. This is a FIXED two-line box with a stated maximum
   height, so it reserves the same space for every user and every name, and the
   empty markup already occupies it before the clock arrives. Reserving a height
   is what the weather panel does for the same reason (D60/F39).

   THE CEILING IS THE LOGO, AND THAT IS WHERE THE FONT SIZE COMES FROM (owner
   ruling). The bar's height is set by the 45px logo mark plus its padding, so
   as long as both lines together fit inside 45px the bar does not grow at all.
   13px at 1.25 gives 16.25px a line, 32.5px for the pair — inside 45px with
   room to spare, and max-height states the guarantee rather than trusting the
   arithmetic to survive a font change.

   display:block, not flex: the two `.aios-word`/username spans are inline and
   flow together as line one, and the date block below is what starts line two.
   A flex column would need a wrapper around the first three spans to group
   them. */
body[data-mode="execution"] .aios-osbar-greeting {
    display: block;
    font-size: 13px;
    line-height: 1.25;
    max-height: 45px;
    overflow: hidden;
}
/* THE ABBREVIATED DAY STAYS, EVEN THOUGH THE LONG ONE NOW FITS. Measured at
   375px once the greeting was stacked: line two has 246px, and the variants
   need 160px (Mon Aug 10th), 201px (Monday August 10th), 237px (short + the
   numeric date) and 278px (long + numeric). So the long day name fits here with
   45px to spare — and it stops fitting around 320px, where the overflow would
   ellipsis the CLOCK, which is the exact failure the flex-row rule above was
   written to end. The short form fits at every width this mode covers, so it is
   the one that is deterministic rather than the one that is comfortable on the
   phone that happened to be measured. */

/* Each line clips on its own. The container's ellipsis covers line one (the
   inline run), and the date block needs its own because it is a separate block
   box — an ellipsis on the parent does not reach into it. */
body[data-mode="execution"] .aios-osbar-greeting > strong {
    display: block;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Chrome the bar can spare at this width, bought back for the greeting: 20px
   from the bar's own side padding and 8px from the gap after the logo. */
body[data-mode="execution"] .aios-osbar { padding: 10px 10px; }
body[data-mode="execution"] .aios-osbar-left { gap: 12px; }
/* "AIOS:" IS THE FIRST THING TO GO, AND THE LOGO IS WHY (owner ruling). The
   mark immediately to its left already says AIOS in the brand's own colours, so
   the word is the redundant one at this width — the mark is not. It is hidden
   outright rather than left to truncate, because a partially-clipped "AIO…"
   beside a logo reading AIOS is worse than no word at all. */
/* SCOPED TO THE BAR, and the descendant selector is why it needed fixing: the
   class is used in THREE places — the system bar, the prompt box's identity
   line and the full-screen prompt's — so an unscoped rule hid "AIOS:" from the
   prompt box on every phone. The argument above is about the BAR (the logo
   mark beside it already says AIOS); nothing about it applies to a heading
   with no logo next to it. */
body[data-mode="execution"] .aios-osbar .aios-osbar-brand { display: none; }
/* And the long day name is hidden ABOVE execution, so exactly one of the pair
   is ever on screen. Without this rule both render and the bar reads
   "Monday August 10thMon Aug 10th". */
.aios-date-day-short { display: none; }
body[data-mode="execution"] .aios-date-day-short { display: inline; }
/* flex:0 0 auto is the SAME trap as #aios-logout-btn above, and this element hit it harder.
   A flex item shrinks by default, and the shrink applies to the main axis ONLY — so inside
   the (min-width:0) .aios-osbar-left, `width:45px` was treated as a starting size the logo
   could be squeezed below while `height:45px` held firm. The result at execution width was a
   45px-tall ellipse a few px wide: the logo was not clipped or scaled, it was SQUISHED, which
   is exactly what the border-radius and the radial gradient then rendered as a smear.
   Reserving the size is not enough on its own; refusing to shrink is the fix. */
.aios-logo-mark {
    flex: 0 0 auto;
    width: 45px; height: 45px; border-radius: 12px; background: radial-gradient(circle at 30% 20%,#2ee6d6,#1a9fa1 55%,#0b3b3c 100%);
    display: flex; align-items: center; justify-content: center; font-weight: 800; color: #fff; font-size: 11px;
}
.aios-osbar-brand { color: var(--aios-accent); font-weight: bold; font-size: 1.1rem; }

/* --- HERO HUD (CENTERED CONTENT) --- */
.aios-hero {
    background: linear-gradient(135deg,rgba(46,230,214,0.08),#000); 
    border-radius: var(--aios-radius); border: 1px solid var(--aios-border);
    /* TIGHTENED, 20px 30px -> 12px 20px (owner ruling). This panel is a
       greeting, two controls and one input; the padding was reading as an
       empty band above the fold on a laptop. The INPUT is untouched - the
       prompt box does not get smaller, only the space around it does. */
    padding: 12px 20px;
    width: 100% !important; box-shadow: var(--aios-shadow); box-sizing: border-box;
    text-align: center;
}
/* The identity row carries the work controls now, so it is a left-aligned row
   rather than a centred one: the title in the corner, the buttons beside it.
   space-between would push them to the far right on a wide panel, which is the
   opposite of "as close to the upper-left as makes sense". */
/* CENTRED, AND AT FULL SIZE. It was briefly left-aligned and sharing a row with
   the work buttons, which pushed the title off centre and shrank it. The
   thinking indicator lives inside this heading, so anything done to it is done
   to the one piece of feedback that says AIOS is working. */
.aios-hero-identity {
    display: flex; align-items: center; justify-content: center;
    gap: 20px; width: 100%; margin-bottom: 8px;
}
/* The work controls get their OWN row above the identity, hard left. Only these
   two belong in that corner (owner ruling). */
.aios-hero-tools {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    justify-content: flex-start; margin-bottom: 6px;
}
/* Small and quiet: these sit next to a 24px title and must not compete with
   it. flex: 0 0 auto because a fixed-size control in a flex row is otherwise a
   starting size, not a size - the mistake this stylesheet keeps making. */
.aios-hero-tools .aios-btn {
    flex: 0 0 auto; font-size: 11px; padding: 5px 10px; letter-spacing: 0.04em;
}
.aios-thinking-box {
    font-family: 'Courier New', monospace; font-size: 24px; color: var(--aios-accent); 
    font-weight: bold; opacity: 0; transition: opacity 0.3s;
    display: none; /* Hidden by default */
    margin-left: 10px;
}
.aios-thinking-active { opacity: 1; animation: pulse 1.5s infinite;display: inline-block; }
@keyframes pulse { 0%,100% { opacity: 0.6; } 50% { opacity: 1; } }


.aios-title { font-size: 24px; font-weight: 700; margin: 0; }
/* No rule existed for this, so it carried the user-agent's ~0.83em top and
   bottom margin - the single biggest piece of slack in the panel. */
.aios-greeting { font-size: 15px; margin: 0 0 8px; opacity: 0.85; }
.aios-greeting-input-row { display: flex; gap: 12px; width: 100%; margin-bottom: 8px; justify-content: center; }
.aios-greeting-input-row input {
    flex-grow: 1; max-width: 800px; background: rgba(0,0,0,0.8); border: 1px solid var(--aios-border); 
    border-radius: 50px; padding: 15px 25px; color: #fff; font-size: 17px; outline: none;
}
.aios-hero-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.aiosprompt { font-size: 24px;}


/* --- NOTEBOOK WORKSPACE --- */
.aios-notes-layout { display: grid; grid-template-columns: var(--aios-sidebar-width, 240px) 10px 1fr; gap: 0; flex: 1; overflow: hidden; }
.aios-notes-list { border-right: 1px solid rgba(255,255,255,0.05); }
/* Checkbox alignment is matched to the Recommended Plan panel. Measured there: a plan checkbox's
   left edge sits 4px from its list container's left border — #aios-plan-list carries .aios-list,
   whose padding:0 kills the UA ul indent, its li adds no padding or border, so all that remains is
   the UA's own 4px margin-left on input[type=checkbox]. Three things put the notes list on that
   same 4px:
     - the ul reset below. #aios-notes-list carries no class at all, so unlike #aios-plan-list it
       never received .aios-list's padding:0 and kept the UA's 40px padding-inline-start. That 40px
       was the entire visible mismatch; it predates the checkbox feature and sat above every li-level
       number, which is why tuning padding/gap/margin on the li never reached it.
     - padding-left:4px on the li, with .aios-context-check still at margin:0.
     - the active-row bar as an inset box-shadow instead of a border-left. A border occupies layout
       width, so it can only avoid shifting the row by being permanently present in transparent
       form — which also spends 3px of the 4px budget. An inset shadow paints inside the content box
       and consumes no width, so the active row cannot shift horizontally. */
.aios-notes-list ul { margin: 0; padding: 0; }
.aios-notes-list li { list-style: none; display: flex; align-items: center; gap: 8px; padding: 12px 12px 12px 4px; font-size: 16px; color: var(--aios-text-muted); border-bottom: 1px solid rgba(255,255,255,0.02); }
.aios-notes-list li.aios-note-active { background: rgba(46, 230, 214, 0.1); color: #fff; box-shadow: inset 3px 0 0 var(--aios-accent); }
.aios-note-row-text { flex: 1; min-width: 0; cursor: pointer; }

/* CONTEXT TOGGLE CHECKBOX (notes list) — visually mirrors .aios-plan-check; kept as its
   own class rather than sharing that rule since the two serve unrelated features. */
.aios-context-check {
    /* margin:0 overrides the UA default (3px 3px 3px 4px) on input[type=checkbox] — that stray
       4px left margin was most of the row's excess left spacing, and it is not reachable by
       tuning the li's padding/gap. */
    appearance: none; -webkit-appearance: none; width: 16px; height: 16px; flex: 0 0 16px; margin: 0;
    border: 1px solid var(--aios-border); border-radius: 5px; background: #000; cursor: pointer; transition: 0.2s; position: relative;
}
.aios-context-check:hover { border-color: var(--aios-accent); }
.aios-context-check:checked { background: var(--aios-accent); border-color: var(--aios-accent); box-shadow: 0 0 8px rgba(46,230,214,0.5); }
.aios-context-check:checked::after {
    content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
    border: solid #0b1010; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.aios-context-check:disabled { opacity: 0.25; cursor: not-allowed; }

/* CONTEXT PANEL */
#panel-context .aios-card-body { max-height: 220px; }
/* Mobile: 220px is a fixed pixel cap on a viewport that varies, and with the three permanent
   anchor rows always present it leaves room for barely two selected notes. A viewport share keeps
   ~8 rows visible on an 812px phone and ~5 on a 568px one, still scrolling internally via
   .aios-scrollable. Dashboard mode (>=1300px) keeps 220px unchanged. */
body[data-mode="execution"] #panel-context .aios-card-body { max-height: 45vh; }

/* ...and a matching FLOOR, so on a phone the panel is a stable 45vh scroll box rather
   than a box that grows.

   It renders a single "Loading context…" row until /notes/list and /context/toggles have
   both settled, then fills. Measured at 412px on a real account: ~40px -> 385px, and
   because execution mode stacks everything in one column, that ~345px of growth pushes
   the weather panel, the next action and the whole notebook down the page at once. It is
   the largest layout shift left on the dashboard — the two container-level shifts
   Lighthouse reports (`article.aios-col-main` and `main.aios-grid`) are both this.

   The cost is dead space for an account with few notes, which is why this is scoped to
   execution: dashboard mode keeps its 220px cap and no floor, so the desktop panel still
   sizes to its content. A phone is where the growth is expensive, because there is a
   whole page below it to move. */
body[data-mode="execution"] #panel-context .aios-card-body { min-height: 45vh; }
.aios-context-panel-list { display: flex; flex-direction: column; gap: 6px; }
.aios-context-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 14px; padding: 6px 8px; border-radius: 8px; background: rgba(255,255,255,0.03); }
/* An anchor row is checkbox + label; the label takes the slack and truncates, and the checkbox
   never shrinks (.aios-context-check is flex:0 0 16px). */
.aios-context-anchor-row { justify-content: flex-start; }
.aios-context-row-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The note NAME in each row opens that note. A real <button> rather than a span with a click
   handler, so it is reachable by keyboard and announced as something you can press.
   THE ROW IS OTHERWISE UNTOUCHED, AND THAT IS THE POINT (owner ruling). The first attempt
   made the whole label the control, which turned every row into a full-width bar — a
   different panel from the one that was asked for. Only the name is the link.
   `display: inline` is doing two jobs: it keeps the button in the text flow beside the
   anchor tag the way the span it replaced was, and it is what lets the wrapper's
   text-overflow ellipsis apply to it, since a button's default inline-block would be
   hard-clipped instead. Everything else is a reset, so the resting appearance is identical
   to before and only the hover says it is clickable.
   THIS RULE WAS SILENTLY DEAD ONCE, and the failure is worth knowing. An edit left a stray
   comment-close token in the middle of this comment; the text after it stopped being a
   comment, ran on into the selector, made the selector invalid, and the browser therefore
   dropped the entire declaration block — leaving raw user-agent buttons in the panel.
   NEVER WRITE A COMMENT-CLOSE TOKEN INSIDE A CSS COMMENT, not even quoted in prose: this
   note itself had to be reworded, because describing the bug literally reproduced it.
   Nothing reports any of this. node --check does not read CSS, csp_hashes.py does not
   either, and every geometry probe still returns sane numbers because layout is unaffected
   by a missing appearance reset. Only looking at the page catches it. */
.aios-context-open-btn {
    appearance: none; -webkit-appearance: none; display: inline;
    background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit;
    text-align: left; cursor: pointer;
}
.aios-context-open-btn:hover, .aios-context-open-btn:focus-visible { color: var(--aios-accent); text-decoration: underline; }
.aios-context-anchor-row .aios-context-row-label { flex: 1 1 auto; min-width: 0; }
.aios-context-row-note { color: var(--aios-text-muted); }
.aios-context-anchor-tag { display: inline-block; font-size: 10px; font-weight: bold; text-transform: uppercase; color: var(--aios-accent); border: 1px solid var(--aios-border); border-radius: 10px; padding: 1px 8px; margin-right: 6px; }
.aios-context-off-btn { padding: 2px 8px; font-size: 11px; border-radius: 50%; line-height: 1; flex: 0 0 auto; }
.aios-context-empty { color: var(--aios-text-muted); font-size: 14px; font-style: italic; }

.aios-notes-viewer { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }
/* THE NOTE TITLE IS ONE LINE, AND BOTH HALVES OF THAT ARE NEEDED.
   The text is `<date> | <display_title>`, and display_title is the note's first BODY line —
   user prose of any length, not a name. aios.js already cuts it to its first sentence, which
   is the half that keeps the heading MEANINGFUL; this is the half that keeps it one line
   whatever survived, because a first sentence can still be long and a paragraph-shaped
   heading pushes the editor down the panel every time such a note is opened.
   flex:0 0 auto for the same reason as .aios-logo-mark: it is a flex child of a column and
   must not be handed the panel's spare height. */
.aios-note-title {
    flex: 0 0 auto; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ------------------------------------------------------------------
   TEXT SIZE IS A USER SETTING, AND THE DEFAULT DIFFERS BY MODE
   ------------------------------------------------------------------
   17px was right on a desktop and too large on a phone, and a single number
   was briefly set to 15px for both — which fixed the phone and broke the
   desktop. That IS the argument for the setting: the correct size depends on
   the screen, the distance and the eyes, and none of those are things this
   stylesheet can measure.

   So there are two parts and they are independent:
     --aios-text-scale   the USER's multiplier, written by the client from the
                         stored preference, 1 when they have not set one.
     the px below        the per-mode DEFAULT, which is what the scale scales.

   Keeping the default per-mode is what stops one preference from having to be
   a compromise between a 27-inch monitor and a phone held at arm's length. */
.aios-note-content {
    flex: 1; padding: 25px; background: #000; border: 1px solid rgba(255,255,255,0.05);
    border-radius: 8px; outline: none; font-family: 'Consolas', monospace;
    font-size: calc(17px * var(--aios-text-scale, 1)); line-height: 1.7; scroll-behavior: smooth;
}

/* --- NOTEBOOK TABS & PROJECTS --- */
.aios-notebook-header { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
#aios-notebook-hint { cursor: pointer; opacity: 0.6; transition: 0.2s; }
#aios-notebook-hint:hover { opacity: 1; }
#aios-notebook-hint.aios-nb-active { opacity: 1; text-shadow: 0 0 10px rgba(46,230,214,0.5); }
.aios-notebook-tabs { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; min-width: 0; }
.aios-nb-tab {
    background: transparent; border: 1px solid var(--aios-border); border-radius: 20px;
    color: var(--aios-text-muted); font-size: 12px; font-weight: bold; text-transform: uppercase;
    padding: 5px 12px; cursor: pointer; transition: 0.2s; white-space: nowrap;
}
.aios-nb-tab:hover { color: #fff; }
.aios-nb-tab.aios-nb-active { color: var(--aios-accent); border-color: var(--aios-accent); background: rgba(46,230,214,0.1); }
.aios-pinned-tabs { display: contents; }
.aios-project-select {
    background: #000; border: 1px solid var(--aios-border); border-radius: 20px;
    color: var(--aios-text); font-size: 12px; font-weight: bold; padding: 5px 10px;
    cursor: pointer; outline: none; max-width: 180px;
}
.aios-pin-btn { padding: 5px 10px; border-radius: 20px; font-size: 11px; }

.aios-cover-btn.aios-cover-active { color: var(--aios-accent); border-color: var(--aios-accent); background: rgba(46,230,214,0.1); text-shadow: 0 0 10px rgba(46,230,214,0.5); }
/* The follow state (D67) is a DIFFERENT state from "this note is the cover", and it reads as
   one — dashed, because the target moves. Two notes can legitimately show a cover mark at
   once: today's note is the cover AND the follow is on, and collapsing them into one style
   would leave the user unable to tell "pinned to this file" from "pinned to whatever today
   is", which is the entire distinction the feature turns on. */
.aios-cover-btn.aios-cover-following { border-style: dashed; }
.aios-notebook-msg { display: none; color: var(--aios-danger); font-family: 'Consolas', monospace; font-size: 13px; margin: -4px 0 8px 0; }

/* Stale-read conflict bar (D103). Unlike .aios-notebook-msg it does not
   auto-hide: the user's unsaved text is sitting in the editor and the bar is
   the only thing saying so, so it stays until they choose.
   [hidden] is repeated because a class selector beats the user agent's
   [hidden] rule and the markup would otherwise be visible to everyone --
   the F60 defect, which shipped a dead control to every non-admin. */
.aios-note-conflict { display: block; border: 1px solid var(--aios-danger); border-radius: 4px; padding: 8px 10px; margin: -4px 0 8px 0; font-family: 'Consolas', monospace; font-size: 13px; color: var(--aios-danger); }
.aios-note-conflict[hidden] { display: none; }
.aios-note-conflict-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.aios-note-conflict-actions button { flex: 0 0 auto; }
.aios-private-ai-msg { color: var(--aios-text-muted); font-size: 13px; font-style: italic; margin: 6px 0; }
.aios-greeting-input-row input:disabled, #aios-intent-submit:disabled { opacity: 0.4; cursor: not-allowed; }

/* --- NOTEBOOK FIND + RELATED NOTES (D77) --- */
.aios-note-find-row { padding: 0 4px 6px; }
#aios-note-find {
    width: 100%; background: rgba(0,0,0,0.6); border: 1px solid var(--aios-border);
    border-radius: 8px; padding: 6px 10px; color: var(--aios-text); font-size: 13px; outline: none;
}
#aios-note-find:focus { border-color: var(--aios-accent); }
.aios-find-hit { list-style: none; }
.aios-find-open {
    display: block; width: 100%; text-align: left; background: transparent;
    border: none; border-bottom: 1px solid rgba(46,230,214,0.08);
    padding: 6px 8px; cursor: pointer; color: var(--aios-text);
}
.aios-find-open:hover { background: rgba(46, 230, 214, 0.08); }
.aios-find-title { display: block; font-size: 13px; color: var(--aios-accent); }
.aios-find-snippet {
    display: block; font-size: 12px; color: var(--aios-text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aios-related-notes { max-width: 800px; margin: 0 auto; text-align: left; }
.aios-related-head { font-size: 12px; color: var(--aios-text-muted); margin: 4px 0 2px; }
.aios-related-row {
    display: flex; align-items: center; gap: 8px; padding: 2px 0; cursor: pointer;
    font-size: 13px;
}
.aios-related-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
    color: var(--aios-text);
}

/* --- LABELS & REFRESH --- */
.aios-refresh-label {
    font-size: 14px; font-weight: bold; color: var(--aios-accent); margin-right: 20px; font-family: 'Consolas', monospace;
}
/* Right align notebook actions */
/* The notebook toolbar takes its OWN width, and the header wraps around it.
   It used to be `flex: 1` — flex-basis 0 — so it received only whatever the
   title and the category tabs left over. Measured at 1280 (overview, a very
   common laptop width): 170px inside an 883px header, which wrapped seven
   buttons into SIX rows and a 257px-tall stack. `flex-basis: auto` asks for the
   content width instead, `min-width` stops it being crushed below a usable size,
   and wrapping the header lets the whole group drop to its own line when there
   genuinely is not room — one clean break instead of six.

   `min(320px, 100%)` rather than a bare 320px: on a 375px phone a hard floor
   pushed the group wider than the viewport (measured — the last button's right
   edge sat past the screen edge). The floor should stop the toolbar being
   crushed, not stop it fitting. */
.aios-notebook-header { flex-wrap: wrap; }
.aios-notes-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; justify-content: flex-end; flex: 1 1 auto; min-width: min(320px, 100%); }

.aios-action-content { font-size: 22px; font-weight: 600; color: var(--aios-text); font-family: 'Consolas', monospace; margin: 0; text-align: center; }

/* --- NEXT ACTION SIZES TO ITS CONTENT; WEATHER NOW RESERVES A KNOWN HEIGHT ---
   Both panels paint a short placeholder and are then replaced by something taller, and both
   briefly carried a hardcoded min-height here (weather 211px, next action 2.4em). Both of
   those were removed: 211px was dead space whenever the third-party widget rendered shorter
   or not at all, and two lines was dead space for a Next Action that is usually one.

   Next Action stays unreserved — it is a line of text, its shift is small, and a floor for
   the two-line case would be visible padding in the common one-line case.

   WEATHER IS RESERVED AGAIN, AND A HARDCODED NUMBER IS NOW THE RIGHT ANSWER. It was wrong
   before for one specific reason: the panel was a weatherwidget.io embed, so any number here
   was a GUESS about a third party's render. The replacement — remembering the height measured
   on the previous load — was the best answer available to that, and it still could not help a
   first-ever visit, which is exactly the case Lighthouse measures cold.

   The panel is first-party now. Its content is fixed in shape: one current-conditions block
   and one four-day strip, both laid out by this stylesheet. The height is not an estimate of
   someone else's widget, it is a statement about markup in this repo, so it cannot be wrong
   while the two agree — and if the layout below changes, this number is edited with it.
   MEASURED against the rendered panel: 220px at dashboard (1440, 680px column) and 282px at
   execution (420). It has moved four times now (240 flat -> 136/189 -> 273/330 -> 220/282)
   and the direction has reversed twice, so treat any number here as this build's measurement
   rather than as a target.

   THE RESERVATION IS DELIBERATELY THE UNWRAPPED HEIGHT, AND IT IS APPROXIMATE IN ONE CASE.
   The head row wraps on flex-basis, which depends on the COLUMN width, while body[data-mode]
   tracks the VIEWPORT — so a narrow overview window (measured: 317px at an 800px viewport)
   is taller than a wide one at the same mode, and no single per-mode number can be right for
   both. Reserving the taller value would leave visible dead space in the common wide case,
   which is the exact failure the original hardcoded 211px was removed for. Reserving the
   shorter one leaves a small one-time shift in the narrow case only. That is the better
   trade and it is a choice, not an oversight. */
#panel-weather { min-height: 220px; }
body[data-mode="execution"] #panel-weather { min-height: 282px; }

/* --- WEATHER (first-party) ---
   Shaped after the weatherwidget.io "weather_one" theme this replaced, because the owner
   liked how that read: a current-conditions block on the left (place, big temperature,
   condition) and a strip of upcoming days beneath it. Icons are inline SVG drawn by aios.js
   and coloured with currentColor, so there is no image request, no external asset, and
   nothing for the CSP to allow. */
/* contain: layout paint — KEPT ON PRINCIPLE, NOT ON A MEASURED WIN, and the difference
   matters because the next person will otherwise trust a number that is not there.

   The reasoning is sound: styleLayout is the dominant cost on this page (Lighthouse
   attributed 1133–1522ms of main-thread styleLayout against ~430–540ms of scripting), this
   subtree animates dozens of elements forever, and containment is exactly the tool for
   scoping that work.

   BUT A 5-RUN A/B COULD NOT SEPARATE IT FROM NOISE: median 91 with containment (87–94)
   against median 90 without (86–97). Lighthouse on this machine swings ~10 points on
   identical code, partly because the panel's own data comes from a live third-party API
   during the trace. So this stays because it is free and correctly scoped, NOT because it was
   shown to help. Do not cite it as a performance fix, and do not spend more time tuning
   against this signal without first making the measurement quieter.

   `layout paint` and NOT `strict`/`size`: size containment would make the panel ignore its
   own content height, and the reserved min-height below is what holds CLS at 0.009. */
#panel-weather {
    padding: 0; overflow: hidden; margin-bottom: 12px; flex: 0 0 auto; position: relative;
    contain: layout paint;
}
/* NO SKY BAND. A 100px band was reserved above the text so the sun and moon could never
   overlap it; the owner's ruling is that the scene is BACKGROUND and behind the text is
   where it belongs, so that height came back out. The arc spans the whole panel again. */
.aios-weather-inner { position: relative; z-index: 1; padding: 12px 14px 10px; display: flex; flex-direction: column; gap: 8px; }

/* THE CONDITION BACKDROP.
   Its own layer under the content, so switching conditions cross-fades instead of the text
   flickering, and so ONE scrim (the ::after below) guarantees contrast rather than every
   gradient having to be dark enough on its own. data-cond is written by aios.js from the
   CURRENT conditions and is re-evaluated on every refresh, which is what makes the panel
   track the weather rather than just report it. */
.aios-weather-bg {
    position: absolute; inset: 0; z-index: 0; opacity: 0; transition: opacity 0.5s ease, background 0.5s ease;
    background: linear-gradient(160deg, #2b3a4a, #10161d);
}
.aios-weather-bg[data-cond] { opacity: 1; }
/* The scrim, and it is deliberately LIGHT.
   Text on this panel is white and a sunny backdrop is the brightest thing in the product, so
   some scrim is needed or the place name and day labels drop below readable contrast. But
   the first pass ran 0.30 -> 0.62 and that was measured wrong on the screen: against the
   card's own near-black it flattened every night condition to "no background at all", which
   defeats the entire point of the backdrop. Text contrast is held by the shadow on the type
   below instead, which costs nothing on the bright conditions and lets the dark ones stay
   visibly coloured. z-index 4 puts it over the sky but UNDER the weather itself — rain and
   snow read as being on this side of the glass, which is most of the window illusion. */
.aios-weather-bg::after { content: ""; position: absolute; inset: 0; z-index: 4; pointer-events: none; background: linear-gradient(180deg, rgba(0,0,0,0.06), rgba(0,0,0,0.34)); }

/* Night variants are INDIGO, not near-black, for the same reason. They have to read as a
   deliberate night sky rather than as an unstyled panel. Dawn and dusk are their own keys
   rather than a filter over "day": the sun sitting on the horizon in a midday-blue sky is
   the single most obviously wrong thing this scene could draw. */
.aios-weather-bg[data-cond="clear-day"]      { background: linear-gradient(180deg, #4aa8f0 0%, #7cc5f5 55%, #b8e2fb 100%); }
.aios-weather-bg[data-cond="clear-dawn"]     { background: linear-gradient(180deg, #2f3f7a 0%, #b3597a 55%, #f6a15c 100%); }
.aios-weather-bg[data-cond="clear-dusk"]     { background: linear-gradient(180deg, #1f2f63 0%, #a04a72 52%, #f2803f 100%); }
.aios-weather-bg[data-cond="clear-night"]    { background: linear-gradient(180deg, #0b1030 0%, #1b2456 60%, #33406f 100%); }
.aios-weather-bg[data-cond="partly-day"]     { background: linear-gradient(180deg, #4e9ad4 0%, #7db4dc 55%, #b3d4e8 100%); }
.aios-weather-bg[data-cond="partly-dawn"]    { background: linear-gradient(180deg, #35406e 0%, #a06585 55%, #e0a173 100%); }
.aios-weather-bg[data-cond="partly-dusk"]    { background: linear-gradient(180deg, #26315f 0%, #8f5878 52%, #d98a5c 100%); }
.aios-weather-bg[data-cond="partly-night"]   { background: linear-gradient(180deg, #101538 0%, #212a56 60%, #38426b 100%); }
.aios-weather-bg[data-cond="cloudy"]         { background: linear-gradient(180deg, #47505e 0%, #6c7583 55%, #98a1ad 100%); }
.aios-weather-bg[data-cond="fog"]            { background: linear-gradient(180deg, #5c626b 0%, #858b93 55%, #b4b9bf 100%); }
.aios-weather-bg[data-cond="rain"]           { background: linear-gradient(180deg, #232f3f 0%, #3d5166 55%, #5e768e 100%); }
.aios-weather-bg[data-cond="snow"]           { background: linear-gradient(180deg, #46536a 0%, #74869e 55%, #aebdcd 100%); }
.aios-weather-bg[data-cond="thunder"]        { background: linear-gradient(180deg, #191430 0%, #352b4f 55%, #4d4066 100%); }
/* Deep space replaces the weather sky entirely rather than tinting it — there is no weather
   in orbit, and half-applying a rain gradient over a nebula reads as a bug, not a choice. */
.aios-weather-bg[data-cond="space"]          { background: radial-gradient(ellipse at 30% 20%, #241a4a 0%, #120e2c 45%, #05040f 100%); }
/* The scrim comes almost all the way off in space: there is no bright sky to fight, and the
   nebulae are the whole point of choosing it. */
.aios-weather-bg[data-cond="space"]::after   { background: linear-gradient(180deg, rgba(0,0,0,0.02), rgba(0,0,0,0.20)); }
.aios-sky-space { z-index: 1; }
.aios-sky-space svg { position: absolute; inset: 0; width: 100%; height: 100%; }
/* The sun/moon still travels its arc in space, because that arc is how the owner reads the
   time at a glance and losing it would cost a real function for a decorative reason. But it
   is scaled well down and dimmed: a full local sun blazing over a nebula reads as two
   pictures pasted together, where a small bright body reads as a distant star. */
.aios-weather-bg[data-cond="space"] .aios-body-wrap { transform: translate(-50%, -50%) scale(0.5); opacity: 0.8; }
.aios-weather-bg[data-cond="space"] .aios-sun-glow { opacity: 0.45; }

/* THE MILKY WAY. One soft diagonal band — it is what turns a scatter of dots into a sky,
   because the eye reads the band as depth and the dots stop looking placed. */
.aios-milkyway {
    position: absolute; left: -25%; top: -30%; width: 150%; height: 130%;
    background:
      radial-gradient(ellipse 52% 13% at 50% 50%, rgba(226,232,255,0.30), rgba(190,205,255,0.10) 45%, rgba(160,180,255,0) 72%),
      radial-gradient(ellipse 34% 8% at 42% 47%, rgba(255,242,220,0.22), rgba(255,235,205,0) 70%);
    transform: rotate(-19deg);
}
/* Constellations sit above the star field but below the sun/moon. Positioned as a box the
   asterism is drawn inside, so its real proportions survive being placed. */
.aios-constellation { position: absolute; pointer-events: none; }
.aios-constellation svg { width: 100%; height: 100%; display: block; }

/* Contrast is carried here rather than by a heavier scrim — see above. */
.aios-weather-inner { text-shadow: 0 1px 4px rgba(0,0,0,0.8); }

/* ---- THE SCENE ----
   Layers, back to front: sky gradient (above) -> stars -> sun/moon -> clouds -> landscape
   -> precipitation/leaves -> scrim -> content. Everything is generated markup or CSS; there
   is no image request anywhere in here, which is what keeps the tightened CSP intact.

   ALL MOTION IS transform/opacity ONLY, so it composites off the main thread and does not
   touch layout — this panel animates continuously behind a text editor and must not cost
   the typing path anything. */
.aios-weather-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.aios-sky-stars  { z-index: 1; }
.aios-sky-body   { z-index: 2; }
.aios-sky-clouds { z-index: 3; }
.aios-sky-land   { z-index: 3; }
.aios-sky-fall   { z-index: 5; }
.aios-sky-flash  { z-index: 6; }

/* Stars. Opacity is set per-star by JS so the field has depth instead of reading as a grid
   of identical dots. */
.aios-star { position: absolute; width: 2px; height: 2px; border-radius: 50%; background: #fff; animation: aios-twinkle 4s ease-in-out infinite; }
.aios-star-lg { width: 3px; height: 3px; box-shadow: 0 0 4px rgba(255,255,255,0.9); }
@keyframes aios-twinkle { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }

/* A shooting star, occasionally, on clear nights only. Pure delight — it is the one element
   here with no informational job at all. */
.aios-shooting { position: absolute; width: 60px; height: 1px; background: linear-gradient(90deg, rgba(255,255,255,0), #fff); opacity: 0; animation: aios-shoot 11s linear infinite; }
@keyframes aios-shoot {
    0%, 92% { opacity: 0; transform: translate(0, 0) rotate(18deg); }
    93% { opacity: 1; }
    100% { opacity: 0; transform: translate(150px, 48px) rotate(18deg); }
}

/* The sun/moon. JS sets left/top from the sun-arc progress, so the body genuinely tracks
   the time of day: up from the lower left, across the top, down to the lower right. */
.aios-body-wrap { position: absolute; transform: translate(-50%, -50%); transition: left 1.2s ease, top 1.2s ease; }
.aios-sun-glow { position: absolute; left: 50%; top: 50%; width: 120px; height: 120px; margin: -60px 0 0 -60px; border-radius: 50%; background: radial-gradient(circle, rgba(255,236,170,0.55), rgba(255,214,120,0.16) 45%, rgba(255,200,90,0) 70%); animation: aios-pulse 6s ease-in-out infinite; }
.aios-moon-glow { position: absolute; left: 50%; top: 50%; width: 78px; height: 78px; margin: -39px 0 0 -39px; border-radius: 50%; background: radial-gradient(circle, rgba(214,228,255,0.34), rgba(190,210,255,0.10) 50%, rgba(180,200,255,0) 72%); animation: aios-pulse 8s ease-in-out infinite; }
@keyframes aios-pulse { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.10); opacity: 1; } }
.aios-body-svg { position: relative; display: block; }

/* Clouds drift. Duration and direction are set inline by JS from the real wind, so a still
   day is genuinely still and a gale actually moves. */
.aios-cloud { position: absolute; opacity: 0.85; animation-name: aios-drift; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes aios-drift { from { transform: translateX(-140px); } to { transform: translateX(460px); } }

/* Rain. A streak rather than a dot, tilted by the wind, with the tilt and duration set
   inline — drizzle and a downpour must not look the same. */
.aios-drop { position: absolute; top: -18px; width: 1.5px; height: 14px; border-radius: 1px; background: linear-gradient(180deg, rgba(190,220,255,0), rgba(200,228,255,0.85)); animation-name: aios-fall; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes aios-fall { to { transform: translateY(260px); } }

/* Snow falls slower and sways, which is the whole difference between snow and white rain. */
.aios-flake { position: absolute; top: -12px; border-radius: 50%; background: #fff; opacity: 0.9; animation-name: aios-flutter; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes aios-flutter {
    0%   { transform: translate(0, 0) rotate(0deg); }
    25%  { transform: translate(11px, 60px) rotate(90deg); }
    50%  { transform: translate(-7px, 120px) rotate(180deg); }
    75%  { transform: translate(9px, 180px) rotate(270deg); }
    100% { transform: translate(0, 250px) rotate(360deg); }
}

/* Leaves, on windy days, tinted by the season. They tumble rather than slide — a leaf that
   translates in a straight line reads as a bug, not as wind. */
.aios-leaf { position: absolute; animation-name: aios-blow; animation-timing-function: linear; animation-iteration-count: infinite; }
@keyframes aios-blow {
    0%   { transform: translate(-40px, 0) rotate(0deg); opacity: 0; }
    10%  { opacity: 0.95; }
    90%  { opacity: 0.95; }
    100% { transform: translate(430px, 34px) rotate(560deg); opacity: 0; }
}

/* Fog rolls as two offset bands so the edges never line up into an obvious rectangle. */
.aios-fogbank { position: absolute; left: -20%; width: 140%; height: 46%; background: linear-gradient(180deg, rgba(255,255,255,0), rgba(226,230,235,0.42), rgba(255,255,255,0)); filter: blur(6px); animation: aios-roll 26s ease-in-out infinite alternate; }
@keyframes aios-roll { from { transform: translateX(-24px); } to { transform: translateX(24px); } }

/* Lightning: two quick strikes then a long wait, because a metronome flash reads as a
   broken element rather than as a storm. */
.aios-sky-flash { background: #fff; opacity: 0; animation: aios-lightning 9s linear infinite; }
@keyframes aios-lightning {
    0%, 3%, 100% { opacity: 0; }
    1%   { opacity: 0.62; }
    1.6% { opacity: 0.10; }
    2.2% { opacity: 0.48; }
}

/* Heat shimmer on a hot clear day — subtle, and the only cue that is about temperature
   rather than conditions. */
.aios-heat { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(255,214,140,0) 55%, rgba(255,198,120,0.20)); animation: aios-shimmer 7s ease-in-out infinite; }
@keyframes aios-shimmer { 0%, 100% { opacity: 0.45; } 50% { opacity: 0.85; } }

/* The horizon. This is what turns a gradient into a VIEW — without a landscape edge the
   panel is an abstract wash, and the sun has nothing to rise from or set behind.
   Back at the panel's foot now the sky band is gone. It sits behind the forecast strip,
   whose cells are translucent, so it reads as depth under them rather than being hidden. */
/* SIZED AS A FRACTION OF THE PANEL, not a pixel band. At a fixed 54px the whole landscape
   sat behind the seven-day strip and was invisible — which made the scenery picker a control
   with no visible effect, the worst kind. At 46% the ridgeline clears the strip, and the
   strip's own cells are translucent so the terrain reads through and between them. */
.aios-sky-land svg { position: absolute; bottom: 0; left: 0; width: 100%; height: 46%; min-height: 96px; display: block; }
/* Ground fill below the horizon, so it is an edge between two solids rather than a
   silhouette floating in mid-sky. */
.aios-sky-land::after { content: ""; position: absolute; left: 0; right: 0; top: auto; bottom: 0; height: 22%; background: linear-gradient(180deg, rgba(10,14,20,0.16), rgba(6,9,14,0.46)); }

/* MOTION IS OFF for anyone who asked for that, and paused when the tab is hidden. An
   animation nobody is looking at is pure battery cost, and this one never stops on its own.
   ENUMERATED, NOT `.aios-weather-bg *`. The universal descendant form matched every node in
   the scene — up to ~200 of them — and made the browser re-evaluate the whole subtree on any
   style invalidation. Listing the eight classes that actually animate costs a longer rule and
   saves the walk. If you add an animated element, add it here; a missed class means an
   animation that ignores prefers-reduced-motion, which is the failure worth avoiding. */
.aios-weather-static .aios-star,
.aios-weather-static .aios-shooting,
.aios-weather-static .aios-cloud,
.aios-weather-static .aios-drop,
.aios-weather-static .aios-flake,
.aios-weather-static .aios-leaf,
.aios-weather-static .aios-fogbank,
.aios-weather-static .aios-sky-flash,
.aios-weather-static .aios-heat,
.aios-weather-static .aios-sun-glow,
.aios-weather-static .aios-moon-glow { animation-play-state: paused !important; }
@media (prefers-reduced-motion: reduce) {
    .aios-star, .aios-shooting, .aios-cloud, .aios-drop, .aios-flake, .aios-leaf,
    .aios-fogbank, .aios-sky-flash, .aios-heat, .aios-sun-glow, .aios-moon-glow,
    .aios-body-wrap { animation: none !important; transition: none !important; }
}

/* Row 1 — conditions left, location control right.
   WRAPS BY FLEX-BASIS, not by a mode rule. The control grew (a wider input, a SET LOCATION
   button and eight scenery circles) and starved the info beside it: measured at 800px the
   place name was clipped to a single letter, "F" for Finger. A body[data-mode] breakpoint
   cannot fix that, because the mode tracks the VIEWPORT while the thing that ran out is the
   COLUMN — the same viewport gives this panel a different width depending on the layout.
   A 240px basis lets the picker drop to its own line exactly when it stops fitting. */
.aios-weather-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.aios-weather { flex: 1 1 240px; min-width: 0; }
.aios-weather-loading, .aios-weather-error { font-size: 12px; opacity: 0.8; padding: 2px 0; }
.aios-weather-error { color: #ffb0b0; opacity: 1; }

/* The temperature now sits INLINE with the place and conditions instead of on the far right
   of its own tall row. That is what reclaimed most of the height: the block went from three
   stacked lines beside a 40px numeral to two lines with the numeral on the baseline. */
/* TYPE IS A STEP UP THROUGHOUT (owner: "that font could be a little bigger on everything").
   The panel had been squeezed to reclaim height and the type went with it; the scene gives
   it back the presence, so the numbers no longer have to whisper. */
.aios-weather-top { display: flex; align-items: center; gap: 12px; }
.aios-weather-icon { flex: 0 0 auto; width: 42px; height: 42px; color: #fff; opacity: 0.98; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.55)); }
.aios-weather-icon svg { width: 100%; height: 100%; display: block; }
/* min-width:0 so a long place name ellipses instead of pushing the temperature out of the
   card — the same flex trap documented on .aios-osbar-left. */
.aios-weather-place-wrap { min-width: 0; }
.aios-weather-place-line { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.aios-weather-place {
    font-size: 14px; font-weight: bold; letter-spacing: 0.06em; text-transform: uppercase;
    color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.aios-weather-temp { flex: 0 0 auto; font-size: 30px; font-weight: 400; line-height: 1; letter-spacing: -0.01em; }
.aios-weather-temp sup { font-size: 14px; vertical-align: super; opacity: 0.8; margin-left: 1px; }
.aios-weather-cond { font-size: 13px; opacity: 0.92; margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* LABELLED METRICS. "92%" on its own is a number with no noun — the label is the fix, and
   it is a separate element rather than baked into the string so the two can size differently:
   the key is small and quiet, the value carries the weight. */
.aios-weather-metrics { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 5px; }
.aios-weather-metric { display: flex; align-items: baseline; gap: 4px; white-space: nowrap; }
.aios-weather-metric-k { font-size: 10px; letter-spacing: 0.07em; text-transform: uppercase; opacity: 0.7; }
.aios-weather-metric-v { font-size: 13px; font-weight: 600; }

/* Row 2 — seven days. TWO LINES, not three: the icon, then the day name and its high/low
   sharing one line (owner request). That removed a row from every cell, which is where most
   of the height came back from, and it let both the name and the temperatures grow. */
.aios-weather-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; border-top: 1px solid rgba(255,255,255,0.20); padding-top: 8px; }
.aios-weather-day {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0;
    padding: 5px 1px 5px; border-radius: 9px; background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.08); transition: transform 0.18s ease, background 0.18s ease;
}
/* The cell is a <button> now, so the UA's font, colour and text-align have to be reset or it
   renders as a system control instead of a forecast cell. The pointer, the lift and the focus
   ring keep it obviously actionable. */
button.aios-weather-day {
    font: inherit; color: inherit; text-align: center; cursor: pointer;
    -webkit-appearance: none; appearance: none;
}
.aios-weather-day:hover { transform: translateY(-2px); background: rgba(255,255,255,0.18); }
button.aios-weather-day:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.45); }
/* Keyboard focus must be visible against a scene of unknown brightness, so it is a solid
   accent ring rather than the UA default outline, which disappears over pale sky. */
button.aios-weather-day:focus-visible { outline: 2px solid var(--aios-accent); outline-offset: 2px; }

/* --- DAY DETAIL CARD ---
   ON <body>, position:fixed, and that is FORCED rather than stylistic: #panel-weather carries
   overflow:hidden and contain:layout paint, either of which clips a descendant. z-index sits
   above the dashboard's panels; it is a transient hover affordance, not part of the layout. */
.aios-day-card {
    position: fixed; z-index: 900; width: 236px; max-width: calc(100vw - 16px);
    padding: 12px 14px; border-radius: 12px;
    background: linear-gradient(160deg, #131a24, #0b0f16);
    border: 1px solid var(--aios-border);
    box-shadow: 0 14px 40px rgba(0,0,0,0.72);
    color: #fff; font-size: 12px; line-height: 1.45;
    /* No text-shadow here: the card has its own solid background, unlike the panel, and
       inheriting the scene's shadow would just blur the type. */
    text-shadow: none;
    animation: aios-daycard-in 0.14s ease-out;
}
@keyframes aios-daycard-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .aios-day-card { animation: none; } }

.aios-day-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.aios-day-card-title { font-size: 12px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--aios-accent); flex: 1 1 auto; min-width: 0; }
.aios-day-card-chip {
    flex: 0 0 auto; font-size: 9px; font-weight: 800; letter-spacing: 0.06em;
    padding: 2px 6px; border-radius: 20px; background: rgba(255,196,84,0.22);
    border: 1px solid rgba(255,196,84,0.6); color: #ffd98a;
}
.aios-day-card-chip.is-severe { background: rgba(255,86,72,0.24); border-color: rgba(255,110,96,0.8); color: #ff9d90; }

.aios-day-card-cond { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; margin-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,0.12); font-size: 13px; }
/* flex:0 0 auto — the icon is a fixed-size flex item, and this stylesheet has already been
   caught out by that four times. */
.aios-day-card-icon { flex: 0 0 auto; width: 30px; height: 30px; }
.aios-day-card-icon svg { width: 100%; height: 100%; display: block; }

.aios-day-card-body { display: flex; flex-direction: column; gap: 3px; }
.aios-day-card-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.aios-day-card-k { font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; opacity: 0.62; white-space: nowrap; }
.aios-day-card-v { font-size: 12px; font-weight: 600; white-space: nowrap; }
.aios-day-card-src { display: block; margin-top: 9px; font-size: 10px; opacity: 0.55; color: inherit; text-decoration: none; }
.aios-day-card-src:hover { opacity: 1; color: var(--aios-accent); text-decoration: underline; }

/* A little pointer toward the cell, flipped when the card had to open downward. */
.aios-day-card::after {
    content: ""; position: absolute; left: 50%; margin-left: -6px; border: 6px solid transparent;
    bottom: -12px; border-top-color: #0b0f16;
}
.aios-day-card.is-below::after { bottom: auto; top: -12px; border-top-color: transparent; border-bottom-color: #131a24; }
.aios-weather-day-today { background: rgba(255,255,255,0.20); border-color: rgba(255,255,255,0.30); }
/* ONE LINE WHEREVER IT FITS, two where it cannot. flex-wrap does this by itself and needs no
   body[data-mode] rule: at dashboard width a cell is ~95px and "MON 94°/74°" sits on one
   line as asked; at 420px a cell is ~50px and the same string overflowed its neighbours
   badly enough to be unreadable, so the temperatures drop underneath. The children keep
   nowrap so a wrap never splits "94°/74°" itself. */
.aios-weather-day-line { display: flex; align-items: baseline; justify-content: center; flex-wrap: wrap; gap: 0 5px; min-width: 0; }
.aios-weather-day-line > span { white-space: nowrap; }
.aios-weather-day-name { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; opacity: 0.9; font-weight: 700; }
.aios-weather-day-icon { position: relative; width: 34px; height: 34px; }
.aios-weather-day-icon svg { width: 100%; height: 100%; display: block; }
.aios-weather-day-temps { font-size: 12px; white-space: nowrap; font-weight: 700; }
.aios-weather-day-low { opacity: 0.62; font-weight: 500; }

/* SEVERITY, NOT JUST CONDITION. data-sev comes from the server's WMO tiering, so freezing
   rain is loud even though it is neither heavy nor a storm — it reads as mild from the
   temperature and it is what puts ice on a road.
   The louder tiers get a warmer border, a ring, and (at 3) a badge and a slow pulse. The
   badge exists because COLOUR ALONE IS NOT A SIGNAL for anyone who cannot separate these
   hues, and this is the one thing on the panel worth not missing. */
.aios-weather-day[data-sev="2"] { border-color: rgba(255,196,84,0.55); box-shadow: inset 0 0 0 1px rgba(255,196,84,0.16); }
.aios-weather-day[data-sev="3"] {
    border-color: rgba(255,110,96,0.85); background: rgba(255,86,72,0.20);
    box-shadow: 0 0 0 1px rgba(255,110,96,0.35), 0 0 14px rgba(255,86,72,0.32);
    animation: aios-sev-pulse 2.4s ease-in-out infinite;
}
@keyframes aios-sev-pulse {
    0%, 100% { box-shadow: 0 0 0 1px rgba(255,110,96,0.32), 0 0 10px rgba(255,86,72,0.24); }
    50%      { box-shadow: 0 0 0 1px rgba(255,140,120,0.75), 0 0 20px rgba(255,86,72,0.55); }
}
.aios-weather-day-warn {
    position: absolute; top: -6px; right: -4px; width: 16px; height: 16px; border-radius: 50%;
    background: #ff5b47; color: #fff; font-size: 11px; font-weight: 800; line-height: 16px;
    text-align: center; box-shadow: 0 0 8px rgba(255,91,71,0.8); text-shadow: none;
}

/* Per-day condition tint. Each cell reads at a glance without being read: a rainy Thursday
   is visibly the odd one out in a sunny week. */
.aios-weather-day[data-icon="clear"]         { background: rgba(255,206,84,0.20); border-color: rgba(255,206,84,0.30); }
.aios-weather-day[data-icon="mostly-clear"]  { background: rgba(255,206,84,0.14); }
.aios-weather-day[data-icon="partly-cloudy"] { background: rgba(150,200,240,0.16); }
.aios-weather-day[data-icon="cloudy"]        { background: rgba(190,200,215,0.16); }
.aios-weather-day[data-icon="fog"]           { background: rgba(200,205,212,0.16); }
.aios-weather-day[data-icon="drizzle"],
.aios-weather-day[data-icon="rain"],
.aios-weather-day[data-icon="showers"],
.aios-weather-day[data-icon="sleet"]         { background: rgba(96,160,220,0.22); }
.aios-weather-day[data-icon="snow"]          { background: rgba(226,238,250,0.24); }
.aios-weather-day[data-icon="thunder"]       { background: rgba(150,110,235,0.26); }
/* The severity tiers must win over the condition tint above — a severe day is a warning
   first and a rain shower second. */
.aios-weather-day[data-sev="3"][data-icon]   { background: rgba(255,86,72,0.20); }

/* Motion ON the icon: real falling drops and flakes over the artwork, so a wet day is
   moving before it is read. Drops are elements rather than SVG animation so they composite,
   and their count and speed come from the severity. */
.aios-weather-day-icon .aios-day-drop {
    position: absolute; top: 56%; width: 2px; height: 6px; border-radius: 1px;
    background: linear-gradient(180deg, rgba(190,225,255,0), #a8d6ff);
    animation-name: aios-day-drop-fall; animation-timing-function: linear; animation-iteration-count: infinite;
}
@keyframes aios-day-drop-fall { 0% { transform: translateY(0); opacity: 0; } 25% { opacity: 1; } 100% { transform: translateY(15px); opacity: 0; } }
.aios-weather-day-icon .aios-day-flake {
    position: absolute; top: 56%; width: 3px; height: 3px; border-radius: 50%; background: #fff;
    animation: aios-day-flake-fall 2.6s linear infinite;
}
@keyframes aios-day-flake-fall { 0% { transform: translate(0,0); opacity: 0; } 20% { opacity: 1; } 100% { transform: translate(3px,14px); opacity: 0; } }
/* The storm flash covers the whole icon, not just the bolt — lightning lights the cloud. */
.aios-weather-day-icon .aios-day-flash {
    position: absolute; inset: -3px; border-radius: 8px; background: radial-gradient(circle at 45% 60%, rgba(255,244,190,0.95), rgba(255,214,90,0) 68%);
    opacity: 0; animation: aios-day-strike 3.6s linear infinite;
}
@keyframes aios-day-strike {
    0%, 62%, 100% { opacity: 0; }
    64% { opacity: 0.95; }
    67% { opacity: 0.15; }
    70% { opacity: 0.75; }
    74% { opacity: 0; }
}

/* Staggered so seven cells never pulse in lockstep, which reads as a loading state. */
.aios-weather-day:nth-child(2) .aios-day-flash { animation-delay: 0.4s; }
.aios-weather-day:nth-child(3) .aios-day-flash { animation-delay: 0.9s; }
.aios-weather-day:nth-child(4) .aios-day-flash { animation-delay: 1.4s; }
.aios-weather-day:nth-child(5) .aios-day-flash { animation-delay: 1.9s; }
.aios-weather-day:nth-child(6) .aios-day-flash { animation-delay: 2.4s; }
.aios-weather-day:nth-child(7) .aios-day-flash { animation-delay: 2.9s; }

/* DESKTOP TYPE IS A FURTHER STEP UP. The previous bump raised every mode together, and the
   owner's point was specifically about the wide layouts — at 680px of column the panel had
   phone-sized type in a space with room to spare, which reads as cramped rather than tidy.
   Execution keeps the smaller scale because there the constraint is real.
   body[data-mode] only; there are no media queries in this stylesheet (F12). */
body[data-mode="dashboard"] .aios-weather-place,
body[data-mode="overview"]  .aios-weather-place       { font-size: 16px; }
body[data-mode="dashboard"] .aios-weather-temp,
body[data-mode="overview"]  .aios-weather-temp        { font-size: 38px; }
body[data-mode="dashboard"] .aios-weather-temp sup,
body[data-mode="overview"]  .aios-weather-temp sup    { font-size: 17px; }
body[data-mode="dashboard"] .aios-weather-cond,
body[data-mode="overview"]  .aios-weather-cond        { font-size: 15px; }
body[data-mode="dashboard"] .aios-weather-metric-k,
body[data-mode="overview"]  .aios-weather-metric-k    { font-size: 11px; }
body[data-mode="dashboard"] .aios-weather-metric-v,
body[data-mode="overview"]  .aios-weather-metric-v    { font-size: 15px; }
body[data-mode="dashboard"] .aios-weather-icon,
body[data-mode="overview"]  .aios-weather-icon        { width: 52px; height: 52px; }
body[data-mode="dashboard"] .aios-weather-day-name,
body[data-mode="overview"]  .aios-weather-day-name    { font-size: 13px; }
body[data-mode="dashboard"] .aios-weather-day-temps,
body[data-mode="overview"]  .aios-weather-day-temps   { font-size: 14px; }
body[data-mode="dashboard"] .aios-weather-day-icon,
body[data-mode="overview"]  .aios-weather-day-icon    { width: 42px; height: 42px; }
body[data-mode="dashboard"] .aios-weather-picker input[type="text"],
body[data-mode="overview"]  .aios-weather-picker input[type="text"] { width: 150px; font-size: 12px; padding: 8px 10px; }
body[data-mode="dashboard"] .aios-weather-picker button,
body[data-mode="overview"]  .aios-weather-picker button { font-size: 10px !important; padding: 8px 11px; }
body[data-mode="dashboard"] .aios-scenery-btn,
body[data-mode="overview"]  .aios-scenery-btn         { width: 28px; height: 28px; }
body[data-mode="dashboard"] .aios-scenery-btn svg,
body[data-mode="overview"]  .aios-scenery-btn svg     { width: 18px; height: 18px; }

/* Same two escapes as the scene: no motion when asked, none when unwatched. Enumerated for
   the same reason as the scene's — see the note there. */
.aios-weather-static .aios-day-drop,
.aios-weather-static .aios-day-flake,
.aios-weather-static .aios-day-flash,
.aios-weather-static .aios-weather-day { animation-play-state: paused !important; }
@media (prefers-reduced-motion: reduce) {
    .aios-day-drop, .aios-day-flake, .aios-day-flash,
    .aios-weather-day { animation: none !important; }
    .aios-weather-day { transition: none !important; }
    /* The badge and the border still carry the warning without any motion. */
    .aios-weather-day[data-sev="3"] { box-shadow: 0 0 0 1px rgba(255,140,120,0.75), 0 0 16px rgba(255,86,72,0.45); }
}

/* The location control. Right-aligned in the head row so the whole setting is done in place,
   with no gear to discover first. */
.aios-weather-picker { flex: 0 0 auto; position: relative; display: flex; flex-direction: column; gap: 5px; align-items: flex-end; }
.aios-weather-picker-row { display: flex; gap: 5px; align-items: center; }
.aios-weather-picker input[type="text"] {
    width: 116px; min-width: 0; padding: 6px 8px; border-radius: 7px;
    border: 1px solid rgba(255,255,255,0.28); background: rgba(0,0,0,0.32); color: #fff; font-size: 11px;
}
.aios-weather-picker input[type="text"]::placeholder { color: rgba(255,255,255,0.55); }
.aios-weather-picker input[type="text"]:focus { outline: 1px solid var(--aios-accent); border-color: var(--aios-accent); }
.aios-weather-picker button { flex: 0 0 auto; font-size: 9px !important; padding: 7px 9px; white-space: nowrap; }
.aios-weather-units { gap: 10px; font-size: 11px; opacity: 0.9; }
.aios-weather-units label { display: flex; align-items: center; gap: 4px; cursor: pointer; }
.aios-weather-units input { width: 12px; height: 12px; }

/* SCENERY BUTTONS — one row of circles under the search box. Glyphs, not words: eight
   labelled buttons would be wider than the panel, and these shapes are recognisable at 24px
   where text is not. The name still reaches assistive tech and the tooltip via aria-label. */
.aios-weather-scenery { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; }
.aios-scenery-btn {
    width: 25px; height: 25px; padding: 0; border-radius: 50%; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.34); border: 1px solid rgba(255,255,255,0.26); color: rgba(255,255,255,0.75);
    transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
/* flex:0 0 auto — THE SAME TRAP AS .aios-logo-mark, and this is its third appearance in
   this stylesheet. The button is a flex container, so the glyph is a flex item and shrinks
   by default: measured at 4px wide against its declared 16px, which is why the buttons
   rendered as bare circles with a hairline in them. A width on a flex item is a starting
   size, not a size. */
.aios-scenery-btn svg { flex: 0 0 auto; width: 16px; height: 16px; display: block; }
.aios-scenery-btn:hover { transform: translateY(-1px); color: #fff; border-color: rgba(255,255,255,0.6); background: rgba(0,0,0,0.5); }
.aios-scenery-btn.is-active {
    color: #04121a; background: var(--aios-accent); border-color: var(--aios-accent);
    box-shadow: 0 0 10px rgba(46,230,214,0.55);
}
@media (prefers-reduced-motion: reduce) { .aios-scenery-btn { transition: none; } }

/* Absolutely positioned so a result list never grows the panel — the height this redesign
   exists to reclaim. Sits above the backdrop and the day strip. */
.aios-weather-results {
    list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px;
    position: absolute; top: 100%; right: 0; z-index: 5; width: 232px; max-height: 168px; overflow-y: auto;
}
.aios-weather-results:not(:empty) { background: #0f1319; border: 1px solid var(--aios-border); border-radius: 8px; padding: 5px; box-shadow: 0 8px 24px rgba(0,0,0,0.6); }
.aios-weather-results li button {
    width: 100%; text-align: left; padding: 7px 9px; border-radius: 6px; cursor: pointer;
    background: transparent; border: 1px solid transparent; color: #fff; font-size: 11px;
}
.aios-weather-results li button:hover { border-color: var(--aios-accent); color: var(--aios-accent); }
.aios-weather-msg { font-size: 10px; color: #ffb0b0; text-align: right; }
.aios-weather-msg[data-ok="1"] { color: var(--aios-accent); }

/* Execution width cannot hold the input, the button and the conditions block side by side —
   measured, the input collapsed to ~40px. The head stacks instead and the control goes full
   width. body[data-mode] only; no media queries in this stylesheet (F12). */
body[data-mode="execution"] .aios-weather-head { flex-wrap: wrap; }
body[data-mode="execution"] .aios-weather-picker { width: 100%; align-items: stretch; }
body[data-mode="execution"] .aios-weather-picker-row { width: 100%; }
body[data-mode="execution"] .aios-weather-picker input[type="text"] { flex: 1 1 auto; width: auto; }
body[data-mode="execution"] .aios-weather-results { width: 100%; }

/* --- SCROLLBARS --- */
.aios-scrollable { overflow-y: auto; flex: 1; min-height: 0; }

/* ------------------------------------------------------------------
   THE HEIGHT CHAIN OVERVIEW AND EXECUTION DO NOT HAVE
   ------------------------------------------------------------------
   `flex: 1` above only divides a height that EXISTS. Dashboard mode supplies
   one - body{height:100dvh} + grid-template-rows:100% - so a scrollable pane
   gets a share of the viewport and scrolls inside it. Overview and execution
   set `height: auto` on the grid, so there is nothing to divide and every
   .aios-scrollable degrades to "grow to content".

   MEASURED, at a 1000px viewport on a real account: the Daily Brief's <pre>
   rendered 23,418px tall, which made its column 24,152px, which made the grid
   row 24,152px - and because the two columns are grid siblings, the OTHER
   column stretched to match. That is why the note viewer read 23,313px and why
   the prompt box was thousands of pixels below the fold. One unbounded pane
   set the height of the whole page.

   The notebook panes were bounded individually further down this file when the
   same thing was found there. The brief, the context body and the roadmap
   panel never were, so this bounds EVERY scrollable pane in the two modes that
   lack the chain, and the pane-specific rules below still win where they need
   a different number (same specificity, later in the file).

   Not a media query: `body[data-mode]` is this stylesheet's only breakpoint
   vocabulary (F12), and the modes are exactly the thing that differs here. */
body[data-mode="overview"] .aios-scrollable,
body[data-mode="execution"] .aios-scrollable { max-height: 60vh; }
/* ------------------------------------------------------------------
   ONE SCROLLBAR EVERYWHERE (owner ruling)
   ------------------------------------------------------------------
   The thin accent bar was scoped to .aios-scrollable, which is the dashboard's
   panel class — so the panels had it and everything else in the product had the
   platform default: a wide grey bar on a dark UI, on the notebook editor, the
   review list, the modals, the admin tables and the page itself. Two scrollbar
   designs in one product reads as an unfinished one.

   This is the same declaration the panels already used, applied to every
   scrolling box on every AIOS page (all four load this stylesheet). The
   .aios-scrollable rules below are now redundant by value and are kept because
   they are what the dashboard's own panels are checked against.

   `height` is set as well as `width`: the original only styled vertical bars,
   so a horizontal one — the admin tables have them — stayed the default.

   scrollbar-width/scrollbar-color are the standard properties and are what
   Firefox reads; the ::-webkit- rules are for Chromium and WebKit. Both are
   needed, and neither is a fallback for the other. Firefox has no equivalent
   of the glow, so it gets the colour and the thinness and not the shadow. */
* { scrollbar-width: thin; scrollbar-color: var(--aios-accent) transparent; }
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--aios-accent); border-radius: 10px; box-shadow: 0 0 10px var(--aios-accent); }
::-webkit-scrollbar-corner { background: transparent; }

.aios-scrollable::-webkit-scrollbar { width: 5px; }
.aios-scrollable::-webkit-scrollbar-track { background: transparent; }
.aios-scrollable::-webkit-scrollbar-thumb { background: var(--aios-accent); border-radius: 10px; box-shadow: 0 0 10px var(--aios-accent); }

/* --- CARDS & HEADERS --- */
.aios-card { 
    background: linear-gradient(145deg,#151515,#101010); 
    border: 1px solid var(--aios-border); border-radius: var(--aios-radius); padding: 15px; display: flex; flex-direction: column; 
    box-shadow: var(--aios-shadow);
}
.aios-card-header { margin: 0 0 10px 0; padding: 0; }
.aios-card-header h3 {
    margin: 0; font-size: 20px; text-transform: uppercase; color: var(--aios-accent); letter-spacing: 1px;
}

/* COLLAPSIBLE CARD (Daily Startup Brief) — state lives in one class on the section and CSS does
   the hiding, the same shape as .aios-view-toggle-active on the Edit/Preview switch and
   .aios-nb-active on the notebook tabs. No new interaction pattern, and because the body is
   hidden by that class rather than by an inline display, a content refresh (which only writes
   textContent) cannot reopen a collapsed panel. The whole header row is the hit area — the arrow
   is an indicator, not the target. */
.aios-collapse-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; cursor: pointer; -webkit-user-select: none; user-select: none; }
/* The row is nowrap and the actions block is flex:0 0 auto, so the controls never actually wrapped
   — the h3 did. As a flex item it inherits min-width:auto, i.e. it refuses to shrink below its
   longest word and wraps its own text instead, taking the row from 34px to 54px and then 81px and
   leaving the ⟳ and chevron beside a two- or three-line heading. min-width:0 lets it shrink; the
   ellipsis is how it degrades once it has to. Measured: the row holds at 34px with the controls
   right-aligned down to 320px and below. */
.aios-collapse-header h3 { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aios-collapse-header:hover h3 { text-shadow: 0 0 10px rgba(46,230,214,0.5); }
.aios-collapse-header-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* display is declared because transform is ignored on a non-replaced inline element. It is
   belt-and-braces today — the arrow is a flex item of .aios-collapse-header-actions, so it is
   already blockified — but it keeps the rotate working if that wrapper ever stops being flex. */
.aios-collapse-arrow { display: inline-block; color: var(--aios-accent); font-size: 14px; line-height: 1; transition: transform 0.2s; }
/* THE EXECUTION-MODE FOLD DEFAULT IS CSS, NOT JAVASCRIPT.
   Each rule below carries a second selector, and the reason is measured. These three panels
   default to folded on a phone, and when JavaScript applied that default at init the page
   painted with the left column fully expanded and then collapsed it — everything below jumped
   up at once. **CLS 0.129 -> 1.056, mobile Lighthouse 91 -> 74**, scored almost entirely
   against `main.aios-grid`. Expressing the default in CSS costs nothing, because `data-mode`
   is already on <body> before the first paint (see the inline block in dashboard.html), so the
   panels are painted folded the first time rather than folded afterwards.

   The state model is therefore three-valued, not boolean:
     .aios-collapsed  — the user folded it, in any mode
     .aios-expanded   — the user opened it, which is what overrides the mode default
     neither          — no choice made yet, so the mode decides
   `:not(.aios-expanded)` is all that is needed on the second selector: a panel carrying
   .aios-collapsed is already matched by the first one. aios.js only ever writes an EXPLICIT
   class, and never at init — init sets aria-expanded only, which cannot shift layout. */
/* THE EXECUTION-MODE FOLD DEFAULT IS GONE (owner ruling, D87), and only the
   default — an explicit .aios-collapsed still folds, on every mode.
   Panels defaulted to folded on a phone because execution mode was ONE LONG
   STACK and a screen of full-height cards meant scrolling past four of them to
   reach the fifth. D81 made each panel its own page, so the premise is gone:
   the user has already chosen the panel by tapping its tab, and handing them a
   collapsed header means every page opens needing one more tap to show the
   thing they just asked for. */
.aios-card.aios-collapsed .aios-collapse-arrow { transform: rotate(-90deg); }
.aios-card.aios-collapsed > .aios-card-body { display: none; }
.aios-card.aios-collapsed .aios-card-header { margin-bottom: 0; }
/* A collapsed panel must stop claiming the column's spare height, or the fold saves nothing. */
.aios-card.aios-fill-card.aios-collapsed { flex: 0 0 auto; }
/* Quick Links' governance row is a SIBLING of the card body, not inside it, so the rule above
   does not reach it — folding the panel would otherwise leave a stray "Governance: Open User
   Manager" line under a collapsed header. It is only ever visible for an admin, which is exactly
   who would notice. !important because the row's visibility is owned by an inline style that
   aios.js writes (display:none -> flex), and a stylesheet rule cannot outrank that otherwise. */
.aios-card.aios-collapsed > #admin-link-row,
/* (The execution-mode half of this rule went with the fold default above — an
   explicitly collapsed panel still hides the row, on every mode.) */

/* STARTUP BRIEF */
.aios-brief-content { 
    white-space: pre-wrap; word-wrap: break-word; word-break: break-word; 
    font-family: monospace; font-size: 16px; line-height: 1.6; color: #eee; 
}
.aios-list { margin: 0; padding: 0; }
.aios-list li { font-size: 16px; margin-bottom: 10px; line-height: 1.4; color: #ddd; list-style: none; }

/* DAILY BRIEF STRUCTURE (D79). The brief is still a <pre> so line layout is
   the text's own; these style the structural spans the renderer inserts. */
.aios-brief-heading {
    color: var(--aios-accent); font-weight: bold; letter-spacing: 1px;
    display: inline-block; margin-top: 6px;
}
.aios-brief-reason {
    font-weight: bold; text-decoration: underline;
    text-underline-offset: 3px; opacity: 0.9;
}
.aios-brief-edit-btn {
    background: transparent; border: 1px solid var(--aios-border); border-radius: 5px;
    color: var(--aios-accent); font-size: 10px; line-height: 1; padding: 2px 8px;
    cursor: pointer; font-family: inherit; vertical-align: middle;
}
.aios-brief-edit-btn:hover { border-color: var(--aios-accent); }

/* Plan panel group headings — the same vocabulary as the brief (D79). */
.aios-plan-group { list-style: none; margin: 8px 0 2px; }
.aios-plan-group-name {
    display: block; color: var(--aios-accent); font-weight: bold; font-size: 13px;
    letter-spacing: 0.5px;
}
.aios-plan-group-reason {
    display: block; font-size: 12px; font-weight: bold; text-decoration: underline;
    text-underline-offset: 3px; color: var(--aios-text-muted);
}

/* RECOMMENDED PLAN CHECKLIST */
.aios-plan-msg { display: none; color: var(--aios-danger); font-family: 'Consolas', monospace; font-size: 13px; margin: -4px 0 8px 0; }
.aios-plan-item-row { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
.aios-plan-check {
    appearance: none; -webkit-appearance: none; width: 16px; height: 16px; margin-top: 3px; flex: 0 0 16px;
    border: 1px solid var(--aios-border); border-radius: 5px; background: #000; cursor: pointer; transition: 0.2s; position: relative;
}
.aios-plan-check:hover { border-color: var(--aios-accent); }
.aios-plan-check:checked { background: var(--aios-accent); border-color: var(--aios-accent); box-shadow: 0 0 8px rgba(46,230,214,0.5); }
.aios-plan-check:checked::after {
    content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
    border: solid #0b1010; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.aios-plan-row-pending { opacity: 0.45; text-decoration: line-through; }
.aios-plan-row-pending .aios-plan-check { pointer-events: none; }
/* Per-item reorder arrows. The li becomes the flex row; the label keeps the text and
   grows, the arrows keep their own size (flex: 0 0 auto — the three-time flex-shrink
   lesson) and only reveal fully on hover/focus so the list stays quiet to read. */
.aios-plan-item { display: flex; align-items: flex-start; gap: 6px; }
.aios-plan-item .aios-plan-item-row { flex: 1 1 auto; min-width: 0; }
.aios-plan-move { display: flex; gap: 2px; flex: 0 0 auto; opacity: 0.35; }
.aios-plan-item:hover .aios-plan-move,
.aios-plan-move:focus-within { opacity: 1; }
.aios-plan-move-btn {
    background: transparent; border: 1px solid var(--aios-border); border-radius: 5px;
    color: var(--aios-accent); font-size: 10px; line-height: 1; padding: 3px 5px; cursor: pointer;
}
.aios-plan-move-btn:hover:not(:disabled) { border-color: var(--aios-accent); }
.aios-plan-move-btn:disabled { opacity: 0.3; cursor: default; }

/* SYSTEM LINKS --- */
.aios-quick-actions-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 10px 0; }
.aios-quick-actions-grid a, .aios-quick-actions-grid button {
    width: 100%; display: flex; align-items: center; justify-content: center; text-align: center; font-size: 15px !important; font-weight: bold;
}
/* A link label is user text of any length, and the grid column is narrow. Without this a
   long label either widened the column (breaking the 1fr 1fr pair) or spilled past the
   pill's border-radius. Ellipsis keeps every button the same size whatever it is called;
   the full label stays available as the title attribute. */
.aios-quick-actions-grid a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; line-height: 1.6; }

/* --- QUICK LINKS EDITOR --- */
/* Only visible while editing. The grid above stays rendered underneath so the user can see
   what they are changing; the editor is a sibling, not a replacement, which is also why
   cancelling needs no re-render of the links themselves. */
.aios-quick-links-editor { display: flex; flex-direction: column; gap: 8px; padding: 4px 0 10px; }
.aios-quick-links-row { display: flex; align-items: center; gap: 6px; }
.aios-quick-links-row input {
    flex: 1 1 auto; min-width: 0; padding: 8px 10px; border-radius: 8px;
    border: 1px solid var(--aios-border); background: rgba(255,255,255,0.04); color: #fff; font-size: 12px;
}
.aios-quick-links-row input:focus { outline: 1px solid var(--aios-accent); border-color: var(--aios-accent); }
.aios-quick-links-row .aios-ql-label { flex: 0 1 38%; }
/* flex:0 0 auto for the same reason .aios-logo-mark needs it — these are fixed-size
   controls in a flex row and must not be squeezed into unclickable slivers by a long
   value in the inputs beside them. 34px square keeps them a usable touch target. */
.aios-quick-links-row .aios-ql-btn {
    flex: 0 0 auto; width: 34px; height: 34px; padding: 0; border-radius: 8px;
    display: flex; align-items: center; justify-content: center; font-size: 14px; line-height: 1;
    background: transparent; border: 1px solid var(--aios-border); color: #fff; cursor: pointer;
}
.aios-quick-links-row .aios-ql-btn:hover:not(:disabled) { border-color: var(--aios-accent); color: var(--aios-accent); }
.aios-quick-links-row .aios-ql-btn:disabled { opacity: 0.3; cursor: default; }
.aios-quick-links-row .aios-ql-remove:hover { border-color: #ff6b6b; color: #ff6b6b; }
.aios-quick-links-editor-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 4px; }
.aios-quick-links-editor-actions button { font-size: 12px !important; padding: 10px 12px; }
.aios-quick-links-add { grid-column: span 2; }
.aios-quick-links-msg { font-size: 11px; padding: 6px 0; color: #ff6b6b; }
.aios-quick-links-msg[data-ok="1"] { color: var(--aios-accent); }
.aios-quick-links-controls { padding-top: 0; }
.aios-quick-links-controls button { font-size: 12px !important; }
/* In execution mode the left column is already stacked above the notebook, so the editor's
   two-up inputs become unusable at phone width — the URL field ends up ~90px. One field per
   line instead. body[data-mode] only; there are no media queries in this stylesheet (F12). */
body[data-mode="execution"] .aios-quick-links-row { flex-wrap: wrap; }
body[data-mode="execution"] .aios-quick-links-row .aios-ql-label { flex: 1 1 100%; }

/* --- BUTTONS --- */
.aios-btn { 
    padding: 14px 20px; border-radius: 50px; cursor: pointer; font-size: 13px; font-weight: bold;
    text-transform: uppercase; border: 1px solid var(--aios-border); background: transparent; color: #fff; transition: 0.2s; text-decoration: none;
}
.aios-btn-primary { 
    background: linear-gradient(135deg,var(--aios-accent),var(--aios-accent-deep)); 
    font-weight: 600; border: none; box-shadow: 0 0 15px rgba(46, 230, 214, 0.4);
}
.aios-btn-primary:hover { transform: translateY(-1px); box-shadow: 0 0 24px rgba(46, 230, 214, 0.6); }

.aios-icon-btn { 
    background: #000; border: 1px solid var(--aios-border); padding: 8px 15px; border-radius: 8px; 
    color: var(--aios-accent); font-weight: bold; cursor: pointer; transition: 0.2s;
}

.aios-notes-resizer { width: 10px; cursor: col-resize; background: transparent; display: flex; justify-content: center; align-items: center; }
.aios-notes-resizer::after { content: ""; width: 2px; height: 40px; background: rgba(255, 255, 255, 0.05); border-radius: 10px; }

/* --- NOTE TOOLBAR: IMAGE UPLOAD + EDIT/PREVIEW TOGGLE --- */
.aios-note-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 8px 0; }
.aios-icon-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.aios-view-toggle { display: flex; gap: 4px; background: #000; border: 1px solid var(--aios-border); border-radius: 20px; padding: 3px; }
.aios-view-toggle-btn {
    background: transparent; border: none; color: var(--aios-text-muted); font-size: 11px; font-weight: bold;
    text-transform: uppercase; padding: 5px 14px; border-radius: 16px; cursor: pointer; transition: 0.2s;
}
.aios-view-toggle-btn:hover { color: #fff; }
.aios-view-toggle-btn.aios-view-toggle-active { color: #000; background: var(--aios-accent); }
.aios-note-preview { white-space: pre-wrap; word-wrap: break-word; word-break: break-word; }
.aios-note-img {
    max-width: 320px; max-height: 320px; border-radius: 6px; border: 1px solid var(--aios-border);
    display: block; margin: 8px 0; box-shadow: 0 0 12px rgba(46,230,214,0.15);
}
.aios-note-img-placeholder {
    display: inline-block; padding: 6px 12px; border: 1px dashed var(--aios-border); border-radius: 6px;
    color: var(--aios-text-muted); font-size: 13px; font-style: italic; margin: 4px 0;
}

/* --- RESPONSIVE NOTEBOOK (execution / overview) ---
   Outside dashboard mode nothing in the chain has a definite height: body is height:auto, so
   .aios-col-main's height:100% resolves to auto and every .aios-scrollable inside the notebook
   degrades to "grow to content" — a 60-line note measured 10,164px tall, with the page scrolling
   in place of the editor. These rules give each pane its own bound. Dashboard mode (>=1300px) is
   untouched: it already has a definite height from body{height:100dvh}. */
body[data-mode="execution"] .aios-notes-layout,
body[data-mode="overview"] .aios-notes-layout { overflow: visible; }

/* Stack: list above viewer. The resizer is a col-resize handle for a side-by-side layout, so it
   is removed from the flow rather than left as a dead 10px strip. */
body[data-mode="execution"] .aios-notes-layout { display: block; }
body[data-mode="execution"] .aios-notes-resizer { display: none; }
body[data-mode="execution"] .aios-notes-list { border-right: none; }
/* FIVE rows, not ten. A single-line row measures 46px at this width (16px text, 12px+12px
   padding, 1px rule), so 230px.

   Two reasons it came down from 460px. The editor is the work area and the list is its index,
   and on a phone they are STACKED — so every pixel the list holds is a pixel the editor does
   not get, and 460px of index above the note is the wrong split. And the list is the notebook's
   only unbounded async filler: it renders empty, then 57 rows arrive and it grows to its cap,
   pushing the editor down by the full height. Measured on a real account: 0 -> 460px.

   min-height matches max-height, so it is a FIXED 230px box that scrolls internally rather than
   one that grows into place — no shift at all, instead of a smaller one. The cost is dead space
   only for an account with fewer than five notes, which is the first session and not the tenth;
   above five rows there is no waste, because the box is full and scrolling. */
body[data-mode="execution"] .aios-notes-list { max-height: 230px; min-height: 230px; }
body[data-mode="execution"] .aios-notes-list ul:not(:empty) { margin-bottom: 12px; }
body[data-mode="overview"] .aios-notes-list { max-height: 60vh; }
/* The viewer caps near the viewport and scrolls inside itself. max-height rather than height so a
   short note doesn't leave a tall empty box, and overscroll-behavior is deliberately left at its
   default so reaching the end of the note continues into the page scroll instead of trapping it. */
body[data-mode="execution"] .aios-notes-viewer,
body[data-mode="overview"] .aios-notes-viewer { height: auto; overflow: visible; }
body[data-mode="execution"] .aios-note-content,
body[data-mode="overview"] .aios-note-content { flex: 0 1 auto; max-height: 60vh; min-height: 200px; }
/* In execution the editor is pinned AT its cap rather than growing from 200px into it. This is
   the one place where reserving space and giving the notebook the most room are the same
   change: the editor is the work area, so a stable box is not dead space even when the
   note is short — it is somewhere to type. Growing 200px -> the cap on every load moved everything
   below the notebook instead. Execution only; overview and dashboard have their own height
   chains and are not stacked.

   THE CAP IS A FULL VIEWPORT ON A PHONE, NOT 60vh (owner ruling). Execution stacks the panels
   and the page scrolls, so the notebook is not competing with anything for height — it is a
   section you scroll to. 60vh gave a 487px editor inside a 1152px panel at 375x812, which spent
   two fifths of the screen on the surrounding chrome at the moment the user is trying to write.
   Sized so that once the editor's top reaches the top of the viewport it fills the screen: the
   40px is the card's own 15px of padding top and bottom plus a little, so the box lands inside
   the panel rather than pushing it wider than the screen.

   dvh, not vh: on a phone the URL bar retracts as you scroll, and vh is locked to the LARGEST
   viewport — so a 100vh editor is taller than the screen for as long as the bar is showing,
   which is exactly when the user first arrives at it. This is the same unit body[data-mode]
   ="dashboard" already uses for the app shell.

   Overview keeps 60vh. It is a two-column layout where the notebook shares the row, and a
   full-viewport editor there would push its neighbour's content off the fold — the F62 failure
   in the other direction. */
body[data-mode="execution"] .aios-note-content {
    min-height: calc(100dvh - 40px);
    max-height: calc(100dvh - 40px);
    /* 15px is the phone default, and it is the ONLY place the smaller number
       belongs. Setting it globally fixed the phone and made the desktop editor
       a step smaller than the interface around it. The user's scale still
       multiplies this, so someone who wants it larger on a phone can have it. */
    font-size: calc(15px * var(--aios-text-scale, 1));
}
/* ------------------------------------------------------------------
   Context audit — what the assistant was actually given on the last turn.
   Lives inside the Context panel because that panel is already the answer to
   "what does the model see"; this is the receipt for the turn that just ran.
   ------------------------------------------------------------------ */
.aios-context-audit {
    margin-top: 10px;
    border-top: 1px solid var(--aios-border);
    padding-top: 8px;
}
.aios-context-audit-toggle {
    width: 100%;
    background: none;
    border: none;
    color: var(--aios-text-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: bold;
    text-align: left;
    padding: 4px 0;
    cursor: pointer;
}
.aios-context-audit-toggle:hover { color: var(--aios-accent); }
.aios-context-audit-body { margin-top: 6px; font-size: 12px; }
.aios-audit-line {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    padding: 2px 0;
    color: var(--aios-text-muted);
}
.aios-audit-line .aios-audit-src { color: var(--aios-text); }
/* A dropped source is the thing the user most needs to be able to see. */
.aios-audit-dropped .aios-audit-src { color: var(--aios-danger); }
.aios-audit-reason {
    display: block;
    font-size: 11px;
    opacity: 0.7;
    padding-left: 10px;
}


/* ---------------------------------------------------------------------------
   AUTH FORM CONTROLS — shared by login.html AND index.html (the enrollment box).

   These two rules lived ONLY inside login.html's inline <style>. index.html
   marks its three registration fields `class="aios-login-input"` and loads
   aios.css, not login.html — so on the enrollment page the class matched
   nothing and the fields fell back to UA defaults. Measured at 375px they were
   19px tall at 13.3px font: below the 44px touch minimum, and below the 16px
   threshold at which iOS Safari zooms the whole page on focus. That is the
   FIRST screen a new user ever touches, and it was the worst-behaving one.

   A class used by two pages belongs in the stylesheet both pages load. The
   duplicates were removed from login.html in the same change and that page was
   measured before and after to prove it renders identically.
   --------------------------------------------------------------------------- */
.aios-login-form { display: flex; flex-direction: column; gap: 20px; }

.aios-login-input {
    width: 100%; height: 55px; padding: 0 20px; border-radius: 50px;
    background: #1a1a1a; border: 1px solid var(--aios-border);
    /* 16px is load-bearing, not a preference: below it iOS Safari zooms the
       page when the field takes focus, which reads as the layout breaking. */
    color: #ffffff !important; font-size: 16px; outline: none;
}
.aios-login-input:focus { border-color: var(--aios-accent); box-shadow: 0 0 15px var(--aios-border); }

/* ---------------------------------------------------------------------------
   OS BAR ACTIONS — Submit Feedback beside Log Out.

   flex:0 0 auto on the wrapper for exactly the reason #aios-logout-btn already
   carries it: .aios-osbar is a flex row whose left side is min-width:0 and
   allowed to give way, and a control group that can shrink absorbs the squeeze
   by wrapping its labels instead — which is what grew the system bar and cost
   0.29 of a 0.296 CLS the last time it happened. Two buttons here, so the trap
   is twice as easy to fall into.
   --------------------------------------------------------------------------- */
.aios-osbar-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
#aios-feedback-btn { flex: 0 0 auto; white-space: nowrap; padding: 14px 18px; }
/* At phone width two full-size pills do not fit beside the greeting, and the greeting is
   what pays: measured at 375px, "SUBMIT FEEDBACK" took it from 169px to 22px — present,
   ellipsised, and saying nothing. Dropping the prefix gives 68px back, which is enough for
   "Hey <name>," to survive. The prefix is hidden rather than a second label being swapped
   in, so the short form stays a substring of the long one and the words have one source.
   body[data-mode] only — there are no media queries in this stylesheet (F12). */
body[data-mode="execution"] #aios-feedback-btn { padding: 14px 14px; font-size: 11px; }
body[data-mode="execution"] #aios-feedback-btn .aios-label-opt { display: none; }

/* ---------------------------------------------------------------------------
   TEXT SIZE (D73) — three buttons, and the middle one is the readout.

   A separate reset control would be a fourth button on a row that already has
   other things on it. The percentage has to be displayed anyway (a scale you
   cannot see is one you cannot judge), so it does the resetting.

   IT LIVES BESIDE THE TEXT IT RESIZES (owner ruling) — the note editor's
   toolbar and the review panel's editor — not in the system bar, which is three
   panels away from anything it changes. There are therefore SEVERAL of these on
   the page, which is why nothing here is an id.

   flex:0 0 auto on the group and nowrap on the readout for the reason this
   stylesheet has now recorded five times: a fixed control inside a flex row is
   given a STARTING size, not a size.
   --------------------------------------------------------------------------- */
.aios-textsize { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.aios-textsize-btn, .aios-textsize-value {
    padding: 6px 8px; font-size: 11px; line-height: 1; white-space: nowrap;
}
/* The readout is a fixed width so stepping through 90% -> 100% -> 110% does not
   shuffle the buttons either side of it under the pointer. */
.aios-textsize-value { min-width: 46px; text-align: center; font-variant-numeric: tabular-nums; }
.aios-textsize-btn:disabled, .aios-textsize-value:disabled { opacity: 0.4; cursor: not-allowed; }

/* The note toolbar is space-between, so IMAGE and TEXT SIZE need a wrapper to
   stay together on the left while Edit/Preview holds the right. A third bare
   child would have spread all three across the row instead. */
.aios-note-toolbar-left { display: flex; align-items: center; gap: 10px; min-width: 0; }

/* The review panel's field header: the label stays exactly where it was and the
   control is pushed to the right edge by space-between rather than by a margin,
   so the label's position does not depend on the control's width. */
.aios-review-field-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; margin-bottom: 6px;
}
.aios-review-field-head label { min-width: 0; }

/* ---------------------------------------------------------------------------
   THE SYSTEM BAR MENU — one toggle on a phone, inline everywhere else.

   The bar's controls used to sit inline at every width. .aios-osbar-left is the
   flex item allowed to give way, so every pixel the buttons took came out of
   the greeting — which is the only thing in the bar carrying information. With
   the text-size group added there were three controls competing with it, and
   the greeting lost by construction.

   The menu is a fixed-position sheet rather than an absolutely-positioned
   dropdown: .aios-osbar has overflow constraints of its own in this layout, and
   a descendant that must escape its ancestor's box is the lesson D65 already
   recorded for the weather day card.

   body[data-mode] rather than a media query, per F12.
   --------------------------------------------------------------------------- */
.aios-osbar-menu-btn { display: none; flex: 0 0 auto; padding: 12px 14px; font-size: 16px; line-height: 1; }
.aios-osbar-menu-btn[hidden] { display: none; }

body[data-mode="execution"] .aios-osbar-menu-btn { display: inline-flex; align-items: center; }
body[data-mode="execution"] .aios-osbar-actions {
    display: none;
    position: fixed; top: 62px; right: 8px; left: auto; z-index: 950;
    flex-direction: column; align-items: stretch; gap: 8px;
    min-width: 210px; padding: 12px;
    background: linear-gradient(145deg,#151515,#101010), var(--aios-bg);
    border: 1px solid var(--aios-border); border-radius: var(--aios-radius);
    box-shadow: var(--aios-shadow);
}
body[data-mode="execution"] .aios-osbar-actions.aios-osbar-open { display: flex; }
/* Inside the sheet the controls are full-width rows, so the tap target is the
   whole row rather than a pill the width of its label. The text-size group
   keeps its own row and spreads across it. */
body[data-mode="execution"] .aios-osbar-actions .aios-btn { width: 100%; justify-content: center; }
/* The text-size overrides that used to live here are gone with the control
   itself, which moved to the note toolbar and the review panel. They are worth
   a line of history because the collision they resolved will recur: the rule
   above is (0,3,1) and a `.aios-textsize-btn` override is (0,2,1), so the
   narrower-looking selector LOST and A- and A+ were each given width:100%
   inside a 210px sheet. Matching the specificity was the fix; !important would
   have hidden the next collision instead of resolving it. */
/* The prefix comes back inside the sheet — there is room for the full label in
   a 210px column, and "SUBMIT FEEDBACK" is the clearer one. */
body[data-mode="execution"] .aios-osbar-actions .aios-label-opt { display: inline; }
body[data-mode="execution"] #aios-feedback-btn { padding: 14px 14px; font-size: 12px; }

/* ---------------------------------------------------------------------------
   MODAL — the Submit Feedback dialog, and the shell for anything like it.

   position:fixed on the OUTER element and the panel centred inside it, rather
   than a transform-centred panel: the notebook's day card learned the same
   lesson from the other direction (D65). A fixed descendant escapes an
   overflow:hidden ancestor, and this markup sits outside .aios-app precisely so
   nothing in the grid can clip it.
   --------------------------------------------------------------------------- */
.aios-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: 20px; }
.aios-modal[hidden] { display: none; }
.aios-modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.72); }
/* The panel is .aios-card as well, so it inherits the product's surface, border and
   shadow rather than restating them — "matches AIOS style" is a structural property
   here, not a colour that has to be kept in sync by hand.
   max-height + an inner scroll: on a short window the dialog must scroll ITSELF,
   never the page behind it. */
.aios-modal-panel { position: relative; width: min(560px, 100%); max-height: min(88vh, 720px); overflow: auto; }
.aios-modal-panel .aios-card-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

.aios-feedback-intro { font-size: 14px; line-height: 1.5; margin: 0 0 14px 0; }
.aios-feedback-form { display: flex; flex-direction: column; gap: 12px; }
.aios-field { display: flex; flex-direction: column; gap: 5px; }
.aios-field > span { font-size: 11px; font-weight: bold; text-transform: uppercase; letter-spacing: 0.06em; color: var(--aios-accent); }
.aios-field input, .aios-field textarea {
    width: 100%; padding: 10px 12px; border-radius: 8px;
    border: 1px solid var(--aios-border); background: rgba(255,255,255,0.04); color: #fff;
    /* 16px, not a smaller "form" size: below it iOS Safari zooms the page on focus,
       the same reason .aios-login-input states it. */
    font-size: 16px; font-family: inherit;
}
.aios-field textarea { resize: vertical; min-height: 110px; line-height: 1.5; }
.aios-field input:focus, .aios-field textarea:focus { outline: none; border-color: var(--aios-accent); box-shadow: 0 0 12px var(--aios-border); }
/* The honeypot. display:none rather than an off-screen position, because a bot that
   reads the computed style is not the threat model here and a visible-but-shifted
   field is one stylesheet accident away from being typed into by a real person. */
.aios-feedback-hp { display: none; }
.aios-feedback-msg { font-size: 13px; line-height: 1.5; padding: 8px 10px; border-radius: 8px; background: rgba(255,255,255,0.04); color: #ff6b6b; }
.aios-feedback-msg[data-ok="1"] { color: var(--aios-accent); }
/* The escape hatch shown on any failed send. Accent-coloured rather than red like the
   message above it, because it is the thing to DO about the error, not part of the error. */
.aios-feedback-fallback { display: inline-block; margin-top: 6px; color: var(--aios-accent); font-weight: bold; }
.aios-feedback-msg[hidden] { display: none; }
.aios-feedback-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 4px; }
.aios-feedback-actions .aios-btn { padding: 12px 20px; }
.aios-feedback-actions .aios-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------------------------------------------------------------------------
   NOTEBOOK, EXPANDED — the full-screen editing mode.

   THE PANEL DOES NOT MOVE IN THE DOM, and that is the whole design. Reparenting
   #panel-notebook into a dialog would have detached a contenteditable that may be
   mid-edit: the caret goes, the selection goes, and the autosave debounce fires
   against a node the browser has re-created. Fixed positioning gets the same
   picture with none of that — the same element, the same handlers, the same
   pending timers, just a different box.

   z-index sits below .aios-modal on purpose. Feedback opened from an expanded
   notebook must land ON TOP of it, not behind it where it cannot be reached.

   `body.aios-notebook-maxed` locks the page scroll, because the expanded panel is
   the scroll surface now and a document scrolling behind a fixed overlay is the
   thing that makes one feel broken.
   --------------------------------------------------------------------------- */
body.aios-notebook-maxed { overflow: hidden; }
.aios-notebook-max {
    position: fixed; inset: 12px; z-index: 900;
    margin: 0; max-height: none; overflow: hidden;
    /* The card is normally translucent over the page; expanded it is the page,
       so it needs an opaque floor of its own or the grid shows through it. */
    background: linear-gradient(145deg,#151515,#101010), var(--aios-bg);
}
/* Height is stated because the grid's own chain (flex:1 1 auto inside a column that
   is height:100%) is gone the moment the element leaves the flow — without this the
   panel collapses to its content and the editor gets no height at all. */
.aios-notebook-max .aios-notes-layout { height: 100%; min-height: 0; }
/* Every mode's height caps are written for a panel sharing a column. Expanded, the
   editor should take whatever is left, in every mode — including execution, where the
   stacked-phone rules otherwise pin it at 60vh inside a full-screen box. */
.aios-notebook-max .aios-notes-viewer { height: 100%; overflow: hidden; }
body[data-mode="execution"] .aios-notebook-max .aios-note-content,
body[data-mode="overview"] .aios-notebook-max .aios-note-content,
.aios-notebook-max .aios-note-content, .aios-notebook-max .aios-note-preview {
    flex: 1 1 auto; min-height: 0; max-height: none;
}
/* The full-screen notebook's compact prompt. Hidden except while the notebook is
   expanded; fixed to the lower-right so it floats over the panel (z 950 sits above
   the panel's 900 and below the modals). Width is bounded so it stays a corner
   control rather than a bar across the working space. */
#aios-max-prompt { display: none; }
body.aios-notebook-maxed #aios-max-prompt {
    /* A COLUMN NOW, not a single row: the dashboard's own button rows are
       tucked onto the input box (owner ruling) - work tools above, quick
       actions below - so the control reads as one piece of furniture instead
       of a bare field floating in the corner. Rounded rectangle rather than a
       pill, because a pill with two button rows inside it is a lozenge. */
    display: flex; flex-direction: column; gap: 6px;
    position: fixed; right: 24px; bottom: 24px; z-index: 950;
    width: min(460px, calc(100vw - 48px));
    padding: 10px 12px; border-radius: 18px;
    background: rgba(0,0,0,0.88); border: 1px solid var(--aios-border);
    box-shadow: var(--aios-shadow);
}
.aios-max-prompt-row { display: flex; gap: 6px; align-items: center; }
/* The tool rows wrap rather than clip: a truncated label on a control with no
   tooltip is unreadable, and sizing them to their own content is what fixed
   the same problem in the review panel (F66). */
.aios-max-prompt-tools { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.aios-max-prompt-tools .aios-btn { padding: 5px 10px; font-size: 10px; flex: 0 0 auto; }
/* margin-left:auto puts CALENDAR in the corner, matching where it sits in the
   hero's toolbar - the same control in the same place on both surfaces. */
.aios-max-prompt-right { margin-left: auto; }
#aios-max-prompt input {
    flex: 1 1 auto; min-width: 0; background: transparent; border: none;
    color: var(--aios-text); font-size: 14px; outline: none; padding: 6px 10px;
}
#aios-max-prompt input:disabled { opacity: 0.4; cursor: not-allowed; }
#aios-max-prompt .aios-btn { padding: 6px 14px; font-size: 12px; }

/* THERE IS NO EXPANDED MODE IN EXECUTION, AND THAT IS A MEASUREMENT, NOT AN OMISSION.
   Expanding is worth it exactly where the notebook is boxed in by other panels, and the
   editor area was measured before and after in all three modes:

     dashboard (1440x900)  398x52  -> 1134x668   36.6x   the case this exists for
     overview  (1000x800)  353x480 ->  694x527    2.2x   and it pulls the panel to the top
     execution ( 375x812)  343x487 ->  319x207    0.4x   WORSE

   Execution already stacks the notebook full-width and pins the editor at 60vh with the page
   scrolling, so there is nothing boxing it in — while a fixed-viewport panel has to fit the
   notebook's own 284px header inside the 812px it gets. A control that makes the thing it
   promises to improve smaller is the "wrong answer with no failure signal" P16 rules against,
   so it is not offered here. updateViewMode() collapses an already-expanded panel on the way
   into this mode, so narrowing the window cannot strand anyone behind a hidden exit. */
body[data-mode="execution"] #aios-notebook-expand { display: none; }
#aios-notebook-expand.aios-expand-active { color: var(--aios-accent); border-color: var(--aios-accent); background: rgba(46,230,214,0.1); }

/* ============================================================
   REVIEW ARTICLES
   ============================================================
   The gate between a drafted guide and the site. Admin only, and the
   button is `hidden` in the markup until the client draws it.

   The panel is WIDER than the feedback dialog because it is a list beside
   an editor rather than a form, and it collapses to one column on a narrow
   viewport. That single media query is here rather than in body[data-mode]
   because a modal is sized by the VIEWPORT, which is the one thing
   data-mode already tracks and would only indirect. */
.aios-review-btn { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; }
/* A CLASS SELECTOR BEATS THE UA'S [hidden] RULE, and the first version of this
   drew the button for every non-admin because of it. The markup said hidden,
   the element was on screen, and it would have 403'd on click - a dead control
   the server refuses, which is the failure this dashboard has removed once
   already. Any `display` on an element that also uses the hidden attribute
   needs this line beside it. */
.aios-review-btn[hidden] { display: none; }
.aios-review-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    background: var(--aios-accent); color: #000; font-size: 11px; font-weight: bold;
    /* A count is a fixed-size thing in a flex row, and a width on a flex item
       is a starting size rather than a size - the mistake this stylesheet has
       now made four times. */
    flex: 0 0 auto;
}
.aios-review-count[hidden] { display: none; }

/* ------------------------------------------------------------------
   THE PANEL OPENS FULL SCREEN, AND IT IS NOT A TOGGLE (owner ruling)
   ------------------------------------------------------------------
   This is a list beside a text editor, which is the same shape the notebook
   has — and the notebook already established that the answer there is as much
   working room as the viewport will give. The difference is that the notebook
   shares a grid with other panels, so expanding it has to be asked for; this
   is a modal and there is nothing else on screen to preserve. A control that
   every user would press every time is not a choice, it is a default that has
   not been set yet, so there is no expand button here.

   Measured at 1440x900 before: panel 1040x647, editor 690x334, with the list
   and the editor sharing one scroll region.

   The panel is a flex column so the body can take the leftover height. Without
   that the grid inside it sizes to content and the whole thing collapses back
   to its natural height inside a full-height box — the same height-chain
   failure F62 records for the dashboard columns. */
.aios-review-panel {
    width: 100%; max-width: none;
    height: 100%; max-height: none;
    display: flex; flex-direction: column;
    /* The panes scroll, not the panel. A panel that scrolls takes the header
       and the message line off screen with it. */
    overflow: hidden;
}
.aios-review-panel .aios-card-header { flex: 0 0 auto; }
.aios-review-msg { flex: 0 0 auto; }

.aios-review-body {
    display: grid; grid-template-columns: minmax(210px, 300px) 1fr; gap: 18px;
    /* stretch, NOT start: the columns have to fill the body's height or they
       cannot own a scrollbar each, which is the whole point of the change. */
    align-items: stretch;
    flex: 1 1 auto; min-height: 0; overflow: hidden;
}

/* THE LIST AND THE EDITOR SCROLL SEPARATELY (owner ruling). They shared the
   panel's single scroll region, so reaching an article near the bottom of a
   long list scrolled the editor away with it, and scrolling the article being
   read moved the list out from under the cursor. Each column is its own flex
   column with its own overflow, and `min-height: 0` on both is what actually
   permits it — a grid item's default min-height is auto, so without it the
   column refuses to shrink below its content and never overflows at all. */
.aios-review-list-col { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.aios-review-detail-col { display: flex; flex-direction: column; min-height: 0; overflow-y: auto; }

.aios-review-hint { margin: 0 0 8px; flex: 0 0 auto; font-size: 12px; }
.aios-review-list { list-style: none; margin: 0; padding: 0; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.aios-review-list li { margin: 0 0 6px; }
.aios-review-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%; padding: 9px 11px; text-align: left; cursor: pointer;
    background: var(--aios-panel); color: inherit; font: inherit;
    border: 1px solid var(--aios-border); border-radius: 6px;
}
.aios-review-item:hover, .aios-review-item:focus-visible { border-color: var(--aios-accent); outline: none; }
.aios-review-item-active { border-color: var(--aios-accent); box-shadow: 0 0 10px var(--aios-border); }
.aios-review-item-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aios-review-none, .aios-review-empty { opacity: 0.7; font-size: 13px; padding: 12px 0; }

.aios-review-pill, .aios-review-state {
    flex: 0 0 auto; font-size: 10px; font-weight: bold; letter-spacing: 0.06em;
    padding: 2px 7px; border-radius: 10px; border: 1px solid currentColor;
}
/* Colour is not the only signal - the words differ too (PENDING / PUBLISHED),
   for the same reason the forecast severity badge exists. */
.aios-review-pill-pending { color: #ffcc66; }
.aios-review-pill-live { color: #7ee787; }

.aios-review-detail-title { margin: 0 0 6px; flex: 0 0 auto; }
.aios-review-meta { margin: 0 0 6px; font-size: 12px; opacity: 0.75; display: flex; gap: 10px; flex-wrap: wrap; flex: 0 0 auto; }
.aios-review-path { font-family: var(--aios-mono, monospace); }

/* The editor takes the height the metadata and the buttons do not. The chain
   has to be unbroken from the column down to the textarea — detail wrapper,
   then the label, then the field itself — because any link that sizes to its
   content stops the growth there and leaves the textarea at its min-height in
   the middle of a full-screen panel. */
#aios-review-detail { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.aios-review-field { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* 14px base, and it takes the user's scale like the note editor does. It was a
   flat 13px, which is a UI size on a surface where the user is reading and
   editing prose — the same mistake as the note editor, one step smaller. This
   is the other place D73's "anywhere it matters" means. */
.aios-review-field textarea {
    flex: 1 1 auto; min-height: 180px; resize: none;
    font-family: var(--aios-mono, monospace);
    font-size: calc(14px * var(--aios-text-scale, 1)); line-height: 1.6;
}
.aios-review-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.aios-review-note { margin: 10px 0 0; font-size: 12px; opacity: 0.75; }
.aios-review-msg { padding: 10px 16px 14px; font-size: 13px; min-height: 1em; }
.aios-review-msg-error { color: #ff8f8f; }

/* ON A PHONE THE PANEL IS THE VIEWPORT. The modal's 20px inset is a frame
   around a centred dialog, and it is worth its cost on a desktop where there is
   room to spare. At 375px it is 40px of the 375 spent on showing the page
   behind a panel nobody wants to see the page behind. The backdrop still
   covers, so the modal still reads as a modal.

   Kept as a media query for the reason the original comment gives: a modal is
   sized by the VIEWPORT, and body[data-mode] tracks exactly that, so routing
   through it would only indirect. */
@media (max-width: 860px) {
    #aios-review-modal { padding: 0; }
    .aios-review-panel { border-radius: 0; border-left: none; border-right: none; }
    /* One column, and the list is capped so the editor always has the majority.
       Two auto rows would split it evenly and hand half a phone screen to a
       list of titles. */
    .aios-review-body { grid-template-columns: 1fr; grid-template-rows: auto 1fr; gap: 12px; }
    .aios-review-list { max-height: 16vh; flex: 0 1 auto; }
    .aios-review-field textarea { min-height: 140px; }
    /* MEASURED AT 375x812: the action buttons took 100px — more than half what
       the editor had — because desktop label widths wrap four of them onto
       three rows.
       THE SPACE COMES FROM THE LIST, NOT FROM THE BUTTONS. Tightening the
       padding did fit them on one row and did give the editor the difference,
       and it also took the tap target from 45px down to 32px — under the ~44px
       both platform guidelines set, on the one device where the input is a
       fingertip. A control that is harder to hit is not working space.
       So they keep their height and the row gives up its horizontal padding
       instead. The list gives up the 4vh.
       NOT equal-width columns: `flex: 1 1 0` fit them in one row and clipped
       OPEN IN NOTEBOOK to "OPEN IN NOTEBO...", which is a label the user cannot
       read on the one screen where there is no tooltip to fall back on. Sized
       to their own content they still fit the row, because the three of them
       are narrower than 345px once the padding is trimmed — and if a future
       label does not fit, they wrap to a second row intact rather than
       truncating. */
    .aios-review-actions { gap: 8px; margin-top: 8px; }
    .aios-review-actions .aios-btn { padding: 14px 10px; font-size: 11px; white-space: nowrap; }
    .aios-review-note { margin-top: 8px; font-size: 11px; }
    .aios-review-hint { font-size: 11px; margin-bottom: 6px; }
}
/* Every article is editable in the panel now, so there is no readonly state to
   style. The disabled rule stays for OPEN IN NOTEBOOK, which genuinely has
   nothing to open until an article has been adopted into a note. */
.aios-review-actions .aios-btn:disabled { opacity: 0.4; cursor: not-allowed; }
#aios-review-view[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   MOBILE PAGES (D81). On a phone the dashboard is PAGES behind a fixed bottom
   nav, not one long stack. Every rule gates on body[data-mode="execution"] so
   data-mode stays the only breakpoint vocabulary (F12); data-mobile-page is
   page STATE, not a breakpoint, and it is set before first paint by the
   inline block in dashboard.html (the F30 reasoning). Each panel hides on
   NON-matching pages rather than being re-displayed on matching ones, so no
   panel's own display value is ever overridden.
   --------------------------------------------------------------------------- */
.aios-mobile-nav { display: none; }
#panel-calendar { display: none; }
.aios-notes-drawer-btn { display: none; }

body[data-mode="execution"] .aios-mobile-nav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 800;
    background: rgba(5,5,5,0.96); border-top: 1px solid var(--aios-border);
    padding: 6px 6px calc(env(safe-area-inset-bottom, 0px) + 6px); gap: 4px;
}
.aios-mobile-nav button {
    /* 44px minimum tap target — the F66 lesson made measurable. */
    flex: 1 1 0; min-height: 44px; background: transparent;
    border: 1px solid transparent; border-radius: 8px;
    color: var(--aios-text-muted); font-family: 'Consolas', monospace;
    font-size: 12px; letter-spacing: 1px; cursor: pointer;
}
.aios-mobile-nav button[aria-pressed="true"] {
    color: var(--aios-accent); border-color: var(--aios-border);
    background: rgba(46,230,214,0.08);
}
body[data-mode="execution"] .aios-app { padding-bottom: 64px; }

body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="brief"]) #panel-daily-brief { display: none; }
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="plan"]) #panel-plan,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="plan"]) #panel-next-action { display: none; }
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="notebook"]) #panel-weather,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="notebook"]) #panel-notebook,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="notebook"]) .aios-hero { display: none; }
body[data-mode="execution"][data-mobile-page="calendar"] #panel-calendar { display: block; }
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="more"]) #panel-quick-actions,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="more"]) #panel-context,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="more"]) #panel-vision,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="more"]) #panel-roadmap,
body[data-mode="execution"][data-mobile-page]:not([data-mobile-page="more"]) #panel-system-status { display: none; }

/* The notebook page's NOTE LIST IS A DRAWER (owner ruling: maximum working
   space, the list one tap away). Hidden by default in execution mode; the
   toggle opens it as a fixed overlay above the page and below the modals. */
body[data-mode="execution"] .aios-notes-drawer-btn { display: inline-block; }
body[data-mode="execution"] .aios-notes-list { display: none; }
/* FULL VIEWPORT (owner ruling). The drawers were inset 12px with the bar and
   nav showing through, which made them read as a panel floating over a page
   rather than the thing you are now looking at — and the toggle that opened
   them sat UNDER the drawer, so on a phone, where there is no Escape key,
   there was no obvious way back out. They cover the screen now and carry
   their own close button (see .aios-drawer-close), which is the control that
   clears them. */
body[data-mode="execution"].aios-notes-drawer-open .aios-notes-list {
    display: block; position: fixed; inset: 0; z-index: 880;
    background: #05080a; border: none; border-radius: 0;
    padding: 56px 12px 12px; overflow: auto; max-width: none;
    /* THE 230px STACK HEIGHT MUST BE RELEASED, all three of it. Execution mode
       pins this list at max-height AND min-height 230px for the stacked phone
       layout — sensible when the list shares the page, and it overrode
       `inset: 0` completely: the drawer computed height 230px with top:0 and
       bottom:0 both honoured. A fixed element with an explicit height ignores
       the offsets, which is exactly the kind of silent override a measurement
       catches and a read-through does not. */
    height: auto; max-height: none; min-height: 0; flex: none;
}

/* --- CALENDAR (D81) --- */
.aios-cal-nav { display: flex; align-items: center; gap: 8px; }
#aios-cal-label {
    font-family: 'Consolas', monospace; font-size: 13px; color: var(--aios-accent);
    min-width: 110px; text-align: center;
}
.aios-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.aios-cal-dow { text-align: center; font-size: 11px; color: var(--aios-text-muted); padding: 4px 0; }
.aios-cal-day {
    min-height: 44px; border: 1px solid rgba(46,230,214,0.12); border-radius: 8px;
    background: transparent; color: var(--aios-text); font-size: 13px; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; padding: 4px 0;
}
.aios-cal-day.aios-cal-today { border-color: var(--aios-accent); }
.aios-cal-day.aios-cal-selected { background: rgba(46,230,214,0.12); }
.aios-cal-dot { font-size: 10px; color: var(--aios-accent); line-height: 1; }
.aios-cal-daylist { margin-top: 10px; }
.aios-cal-daylist-head { font-weight: bold; color: var(--aios-accent); font-size: 13px; margin-bottom: 4px; }
.aios-cal-event {
    display: flex; gap: 8px; font-size: 13px; padding: 3px 0;
    border-bottom: 1px solid rgba(46,230,214,0.08);
}
.aios-cal-event-time { color: var(--aios-accent); font-family: 'Consolas', monospace; flex: 0 0 auto; }
.aios-cal-empty { color: var(--aios-text-muted); font-size: 13px; }

/* --- CALENDAR EDITING (D102) ---
   The day list is where an event is added, changed and removed, so its head row
   carries the ADD control and each event row carries its own two.

   flex: 0 0 auto on every fixed-size control in these rows, because a width on a
   flex item is a starting size and not a size - this stylesheet has been bitten
   by that three times already (the logo mark, the quick-link labels, the scenery
   glyphs). */
.aios-cal-daylist-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.aios-cal-add { flex: 0 0 auto; }
.aios-cal-event-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aios-cal-event-tools { display: flex; gap: 4px; flex: 0 0 auto; }
.aios-cal-event-note { color: var(--aios-text-muted); font-size: 11px; flex: 0 0 auto; }
.aios-cal-form {
    display: flex; flex-direction: column; gap: 6px; padding: 8px 0;
    border-bottom: 1px solid rgba(46,230,214,0.18);
}
.aios-cal-form-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.aios-cal-form-dash { color: var(--aios-text-muted); flex: 0 0 auto; }
.aios-cal-input {
    background: rgba(0,0,0,0.35); border: 1px solid rgba(46,230,214,0.3); border-radius: 3px;
    color: var(--aios-text); font-family: 'Consolas', monospace; font-size: 13px;
    padding: 4px 6px; flex: 0 0 auto; min-width: 0;
}
.aios-cal-input-title { flex: 1 1 100%; }
.aios-cal-msg { font-size: 12px; color: var(--aios-text-muted); margin: 2px 0 4px 0; }
.aios-cal-msg.aios-cal-msg-error { color: var(--aios-danger); }
/* The message line uses the hidden attribute; a class selector setting display
   would beat the user agent's [hidden] rule and leave it permanently visible.
   That is F60, which shipped a dead control to every non-admin. */
.aios-cal-msg[hidden] { display: none; }


/* ---------------------------------------------------------------------------
   DESKTOP CALENDAR (owner ruling). The calendar was a mobile page only; the
   desktop had no route to it at all. The CALENDAR button sits in the hero's
   tool row, pushed to the upper-right corner by margin-left:auto — the work
   tools keep their place on the left. It opens #panel-calendar as a fixed
   overlay (same mechanism as the expanded notebook: fixed + inset + opaque
   floor, z 900 below the modals). In execution mode the button hides — the
   bottom nav's CAL is the way in there, and two entrances to one page on a
   phone is one too many.
   --------------------------------------------------------------------------- */
#aios-open-calendar { margin-left: auto; }
body[data-mode="execution"] #aios-open-calendar { display: none; }
.aios-cal-close { display: none; }
body.aios-cal-open #panel-calendar {
    display: flex; flex-direction: column;
    position: fixed; inset: 60px 12px 12px; z-index: 900;
    margin: 0; max-height: none; overflow: hidden;
    background: #0b0f14;
}
body.aios-cal-open #panel-calendar .aios-card-body { flex: 1 1 auto; min-height: 0; }
body.aios-cal-open .aios-cal-close { display: inline-block; }

/* THE AI VIEW (D85). Read-only rendering of exactly what the server would
   send for this note. Styled as a quotation of machine input rather than as
   the note: monospace, dimmer, with the character counts on top so the cost
   of a turn is visible before it is spent. */
.aios-note-aiview { white-space: pre-wrap; }
.aios-aiview-head {
    font-size: 12px; color: var(--aios-accent); font-family: 'Consolas', monospace;
    border-bottom: 1px solid var(--aios-border); padding-bottom: 6px; margin-bottom: 8px;
}
.aios-aiview-body {
    font-family: 'Consolas', monospace; white-space: pre-wrap; opacity: 0.88;
}

/* ---------------------------------------------------------------------------
   MOBILE PAGES FILL THE VIEWPORT, UNDER A LOCKED BAR (D87, extended 2026-08-16)
   ---------------------------------------------------------------------------
   THE SYSTEM BAR IS FIXED ON A PHONE, and that is a fix rather than a polish
   choice. It scrolled with the page, so on the Plan page the bar rode up and
   down as the list moved and the layout appeared to glitch — and worse, every
   page's height arithmetic was computed against a bar that might or might not
   be on screen, so "fill the viewport" meant different things depending on
   scroll position. Pinning it makes the available height a CONSTANT, which is
   what the page rules below can then be written against.

   The chrome is the bar plus the bottom nav; stated once as variables so five
   page rules cannot each carry a copy of a number that would drift. `dvh`, not
   `vh`: vh is locked to the LARGEST viewport, so a 100vh page is taller than
   the screen while the URL bar shows — exactly when the user first arrives
   (the F66 lesson, applied to every page rather than only the editor).
   --------------------------------------------------------------------------- */
body[data-mode="execution"] {
    --aios-osbar-h: 66px;
    --aios-nav-h: 62px;
    --aios-mobile-chrome: calc(var(--aios-osbar-h) + var(--aios-nav-h));
}
body[data-mode="execution"] .aios-osbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 820;
    height: var(--aios-osbar-h);
}
/* The bar is out of the flow now, so the app has to reserve its height or the
   first panel starts underneath it — the same zero-height-header trap the
   notebook's own CSS documents for the marketing navbar. */
body[data-mode="execution"] .aios-app { padding-top: var(--aios-osbar-h); }

body[data-mode="execution"][data-mobile-page="brief"] #panel-daily-brief,
body[data-mode="execution"][data-mobile-page="plan"] #panel-plan,
body[data-mode="execution"][data-mobile-page="calendar"] #panel-calendar {
    display: flex; flex-direction: column;
    height: calc(100dvh - var(--aios-mobile-chrome));
    max-height: none; margin-bottom: 0;
}
/* The height chain has to reach the scrolling box or it stops at the header
   and the body keeps its content height inside a full-screen card (the F66
   unbroken-chain lesson). */
body[data-mode="execution"][data-mobile-page="brief"] #panel-daily-brief > .aios-card-body,
body[data-mode="execution"][data-mobile-page="plan"] #panel-plan > .aios-card-body,
body[data-mode="execution"][data-mobile-page="calendar"] #panel-calendar > .aios-card-body {
    flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto;
}
/* The plan page carries Next Action above the list, so the list takes what is
   left rather than the whole screen. */
body[data-mode="execution"][data-mobile-page="plan"] #panel-plan {
    height: calc(100dvh - var(--aios-mobile-chrome) - 84px);
}

/* --- THE NOTEBOOK PAGE IS A SCROLL, NOT A SQUEEZE (owner ruling) ------------
   It was unusable: weather, editor and prompt were each given a share of one
   screen, which left the editor — the thing the page exists for — a few lines
   tall with two panels crowding it. The owner's shape is a scroll instead: the
   EDITOR fills the viewport under the locked bar, you scroll UP to reach the
   weather and DOWN to reach the prompt. Nothing is hidden and nothing is
   crushed; the working area simply gets the screen it is on.
   --------------------------------------------------------------------------- */
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook {
    display: flex; flex-direction: column;
    height: calc(100dvh - var(--aios-mobile-chrome));
    max-height: none; margin: 0 0 12px 0;
}
/* THE LAYOUT MUST BE A FLEX COLUMN HERE, not merely a flex ITEM. In execution
   mode it is display:block (the phone stacks the list and the viewer), and a
   BLOCK parent gives its children no height to fill — so `flex: 1 1 auto` on
   the viewer meant nothing and it grew to its content. Measured: the editor
   rendered 1455px inside a 684px panel. That is F54's "execution has no height
   chain of its own" trap, hit again one element lower down. */
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook .aios-notes-layout {
    display: flex; flex-direction: column;
    flex: 1 1 auto; min-height: 0; height: auto;
}
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook .aios-notes-viewer {
    flex: 1 1 auto; min-height: 0; height: auto;
}
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook .aios-note-content,
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook .aios-note-aiview {
    flex: 1 1 auto; min-height: 0; max-height: none;
}
/* Weather sits above the editor at its own natural height. It is reachable by
   scrolling up rather than by giving it a permanent share of the fold. */
body[data-mode="execution"][data-mobile-page="notebook"] #panel-weather {
    margin-bottom: 12px;
}

/* The full-screen prompt's identity line — the hero's greeting, relocated
   (expanded, the hero is off screen and the thinking indicator inside it had
   nowhere to appear). Sized down: this is a label above an input, not a page
   heading. */
.aios-max-prompt-identity {
    display: flex; align-items: center; gap: 6px;
    font-size: 13px; color: var(--aios-text); padding: 0 4px;
}
.aios-max-prompt-identity .aios-osbar-brand { color: var(--aios-accent); font-weight: bold; }
#aios-max-username { font-weight: bold; }
#aios-max-thinking:empty { display: none; }

/* ---------------------------------------------------------------------------
   WEATHER: SMALL BY DEFAULT, EXPANDABLE (owner ruling, 2026-08-16)
   ---------------------------------------------------------------------------
   The location search was permanently on screen and it is the widest control
   in the panel — three rows of chrome above a scene that is mostly there to be
   looked at. It is behind 📍 now, on the SAME ROW as the scenery buttons, so
   the panel's fixed cost is one row. The panel also collapses to its header;
   DEFAULT EXPANDED, because the scene is the point of it and a collapsed
   default would hide the feature from someone who never pressed anything.
   --------------------------------------------------------------------------- */
.aios-weather-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.aios-weather-controls .aios-weather-scenery { flex: 1 1 auto; min-width: 0; }
.aios-weather-ctl { flex: 0 0 auto; padding: 4px 9px; font-size: 13px; line-height: 1; }
/* Collapsed: the SEVEN-DAY STRIP goes, the head row survives (it carries the
   controls that get it back) — and THE SCENERY STAYS (owner ruling). The
   first cut hid the background with the forecast, which threw away the thing
   the panel is for: the scene is not decoration on top of the weather, it IS
   the weather, told in a picture. Collapsed it becomes a band of sky behind
   the conditions instead of disappearing.
   The panel's stated min-height must go, or the reservation keeps the space
   the collapse was meant to free (D60/F39's height is written for the
   expanded panel). */
#panel-weather.aios-weather-collapsed .aios-weather-days { display: none; }
#panel-weather.aios-weather-collapsed { min-height: 0 !important; }
body[data-mode="execution"] #panel-weather.aios-weather-collapsed { min-height: 0 !important; }

/* ---------------------------------------------------------------------------
   MOBILE: THE MAIN PROMPT BOX, AND CONTEXT AS A DRAWER
   ---------------------------------------------------------------------------
   THE PHONE USES THE REAL PROMPT BOX (owner ruling, reversing the previous
   attempt). It was shrunk to the full-screen notebook's compact control —
   smaller padding, smaller input, smaller buttons, identity line hidden — on
   the reasoning that the notebook page needed the height. It read wrong: the
   compact control is a corner utility that exists because the hero is off
   screen behind an expanded panel, and on the phone the hero is not off
   screen, it IS the prompt box. Shrinking the product's main input to look
   like its fallback was the wrong trade, and the notebook page got its height
   back from the header instead (see the header rules above), which is where
   the waste actually was.

   The greeting HEADING stays hidden, and everywhere rather than only here:
   the sentence is the input's placeholder now (owner ruling), so the <h2>
   would ask the question twice and charge a line of height for the repeat.
   --------------------------------------------------------------------------- */
.aios-greeting-heading { display: none; }

/* CONTEXT is a drawer on the notebook page, opened from the button beside
   NOTES — the same shape as the note list, because it is the same kind of
   thing: a list you consult while writing and dismiss when you are done. */
#aios-context-drawer-btn { display: none; }
body[data-mode="execution"][data-mobile-page="notebook"] #aios-context-drawer-btn { display: inline-block; }
body[data-mode="execution"][data-mobile-page="notebook"] #panel-context { display: none; }
body[data-mode="execution"][data-mobile-page="notebook"].aios-context-drawer-open #panel-context {
    display: flex; flex-direction: column;
    position: fixed; inset: 0; z-index: 880;
    margin: 0; max-height: none;
    background: #05080a; border: none; border-radius: 0;
    overflow: hidden; padding-top: 44px;
}
body[data-mode="execution"][data-mobile-page="notebook"].aios-context-drawer-open #panel-context > .aios-card-body {
    flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto;
}
/* The note-list drawer sits under the LOCKED bar rather than at a hardcoded
   76px, which was measured against a bar that used to scroll away. */
/* (The inset override that used to live here is gone: the drawer is full
   viewport now, so there is nothing to offset it from.) */

/* ---------------------------------------------------------------------------
   CALENDAR: ROOM TO SAY WHAT IS IN THE DAY (owner ruling, 2026-08-16)
   ---------------------------------------------------------------------------
   The desktop cells were phone-sized squares carrying a dot, so the panel used
   a full-screen overlay to show 42 numbers and made the user click each one to
   find out what was in it. Given the width, the cell can just say it.
   The PHONE keeps the dot-and-count: there the cell genuinely is too small,
   and the day list underneath is the detail view.
   --------------------------------------------------------------------------- */
.aios-cal-grid { gap: 6px; }
.aios-cal-daynum { font-weight: bold; }
/* Days from the neighbouring months are real, clickable cells - dimmed so the
   month still reads as a month, not deleted, so a full week always does. */
.aios-cal-outside { opacity: 0.42; }
.aios-cal-outside .aios-cal-daynum { font-weight: normal; }
.aios-cal-cell-events { display: none; }

/* Desktop and overview: taller cells, top-aligned, with the day's events in
   them (D106, owner ruling: "properly sized day boxes").

   HEIGHT IS FIXED, NOT min-height, AND THAT IS THE CHANGE. It was a min-height,
   so a busy day grew its cell, which grew its ROW, which moved every other week
   down the page - the grid changed shape depending on what was in it, and six
   rows of that is a calendar that never looks the same twice. A stated height
   plus a scrolling event list inside gives every cell the same box and still
   shows everything (see .aios-cal-cell-events below). Same reasoning as the
   weather panel's stated height (D60/F39): reserve the space, do not let the
   content set it. */
body:not([data-mode="execution"]) .aios-cal-day {
    height: 118px; align-items: stretch; justify-content: flex-start;
    padding: 6px 7px; text-align: left; gap: 3px; overflow: hidden;
}
/* The events inside a cell scroll. min-height: 0 is what actually permits it -
   a flex child defaults to min-height: auto and refuses to shrink below its
   content, so without it the list never overflows and never gets a bar. Exactly
   the trap the review panel's columns hit (F66). */
body:not([data-mode="execution"]) .aios-cal-cell-events {
    display: flex; flex-direction: column; gap: 2px; width: 100%; min-width: 0;
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
}
body:not([data-mode="execution"]) .aios-cal-cell-event {
    font-size: 11px; line-height: 1.25; color: var(--aios-text);
    background: rgba(46,230,214,0.10); border-left: 2px solid var(--aios-accent);
    border-radius: 3px; padding: 1px 4px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    /* flex: 0 0 auto — THE FOURTH TIME THIS STYLESHEET HAS BEEN BITTEN BY IT
       (after .aios-logo-mark, the quick-link labels and the scenery glyphs), and
       the first in the vertical direction. Inside a flex COLUMN the default
       flex-shrink: 1 applies to HEIGHT: ten event rows in a 86px list silently
       compressed to fit instead of overflowing, so the container never scrolled
       and every line was squashed. Measured: scrollHeight == clientHeight == 86
       with ten children in it. A height on a flex item is a starting size, not a
       size. */
    flex: 0 0 auto;
}
body:not([data-mode="execution"]) .aios-cal-cell-more {
    font-size: 10px; color: var(--aios-text-muted);
}
/* With the events in the cell the dot is a duplicate, so it goes - on the
   phone, where the events are hidden, it is the only signal and stays. */
body:not([data-mode="execution"]) .aios-cal-dot { display: none; }

/* --- FULL WEEKDAY NAMES, AND A SCROLLING DAY LIST (D106, owner ruling) ---
   Both label forms are in the markup and the stylesheet picks one, so the clock
   and the grid never have to ask which mode they are in - the breakpoint lives
   in one place (F12). */
.aios-cal-dow-long { display: none; }
.aios-cal-dow-short { display: inline; }
body:not([data-mode="execution"]) .aios-cal-dow {
    font-size: 12px; letter-spacing: 0.5px; font-weight: bold; padding: 6px 0 8px;
}
body:not([data-mode="execution"]) .aios-cal-dow-long { display: inline; }
body:not([data-mode="execution"]) .aios-cal-dow-short { display: none; }

/* THE DAY LIST SCROLLS INSTEAD OF GROWING THE PANEL. A busy day used to push
   the grid off the top of the phone and off the bottom of the desktop overlay -
   the list is the detail view, and a detail view is not allowed to evict the
   thing it is detailing. A stated max-height rather than flex, because the
   overlay and the mobile page give this element different parents and only one
   of them offers a height to divide. */
.aios-cal-daylist { max-height: 34vh; overflow-y: auto; }
body:not([data-mode="execution"]) .aios-cal-daylist { max-height: 30vh; }
/* The head row stays put while the events scroll under it: it carries the date
   and the ADD control, and losing the date while scrolling a list of times is
   how you add an event to the wrong day. */
.aios-cal-daylist-head {
    /* --aios-surface, a real token, not a made-up one with a fallback: a
       variable that resolves to nothing is a rule that silently does something
       else, and a transparent sticky header over a scrolling list is unreadable
       rather than obviously broken. */
    position: sticky; top: 0; z-index: 1; background: var(--aios-surface);
    padding-bottom: 4px;
}

/* ---------------------------------------------------------------------------
   THE NOTEBOOK PAGE'S HEADER, CUT DOWN TO WHAT A PHONE NEEDS
   ---------------------------------------------------------------------------
   MEASURED, and it is why the page was unusable: the header rendered 300px of
   a 684px page — the category tabs (60px) plus an action row that wrapped to
   193px — leaving 342px for the layout and 234px for the editor. The working
   area got a third of the screen it is supposed to BE.

   So the header keeps only what is used WHILE WRITING: NOTES, CONTEXT, SAVE,
   new note, and the panel title. Everything else is browsing or filing, which
   is what the notes drawer is for — and the tabs and project filter REAPPEAR
   with that drawer, because that is the moment they mean something. Nothing is
   removed from the phone; it is moved to the surface that owns it.
   --------------------------------------------------------------------------- */
body[data-mode="execution"][data-mobile-page="notebook"] #panel-notebook .aios-card-header {
    row-gap: 4px;
}
/* PROJECTS STAY VISIBLE (owner ruling). It went behind the drawer with the
   rest of the browsing controls, and that was one step too far: a project is
   the scope you are WORKING IN, not a thing you look up — the filter tells you
   which notebook you are writing into, so hiding it hides where you are. One
   compact row, ~28px, which is affordable now the header is 64px instead of
   300px. The category TABS stay in the drawer: those genuinely are browsing. */
/* THE TAB BUTTONS HIDE, NOT THEIR CONTAINER. #aios-notebook-tabs is the
   wrapper that also holds the PROJECT SELECT, so hiding the container took
   the project filter with it — measured: display:flex on the select, height
   0, parent display:none. Hide the buttons instead and the select survives. */
body[data-mode="execution"][data-mobile-page="notebook"] #aios-notebook-tabs .aios-nb-tab,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-notebook-tabs .aios-pinned-tabs,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-refresh-timestamp,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-context-select-all,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-context-clear-all,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-set-cover,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-cover-today,
body[data-mode="execution"][data-mobile-page="notebook"] #aios-delete-note { display: none; }

/* Browsing controls come back with the drawer they belong to. */
body[data-mode="execution"][data-mobile-page="notebook"].aios-notes-drawer-open #aios-notebook-tabs .aios-nb-tab {
    display: inline-block;
}
body[data-mode="execution"][data-mobile-page="notebook"].aios-notes-drawer-open #aios-notebook-tabs .aios-pinned-tabs {
    display: flex;
}
/* The wrapper wraps on a phone so the project row sits under the tabs when
   both are showing, rather than the two fighting for one line. */
body[data-mode="execution"][data-mobile-page="notebook"] #aios-notebook-tabs {
    flex-wrap: wrap; gap: 6px;
}
/* The project row is compact on a phone: the select takes the width and its
   two management buttons sit beside it rather than wrapping to their own row. */
body[data-mode="execution"][data-mobile-page="notebook"] .aios-project-select {
    display: flex; flex-wrap: nowrap; gap: 6px; width: 100%;
}
body[data-mode="execution"][data-mobile-page="notebook"] .aios-project-select select {
    flex: 1 1 auto; min-width: 0;
}
/* Context bulk actions come back with the CONTEXT drawer, same rule. */
body[data-mode="execution"][data-mobile-page="notebook"].aios-context-drawer-open #aios-context-select-all,
body[data-mode="execution"][data-mobile-page="notebook"].aios-context-drawer-open #aios-context-clear-all {
    display: inline-block;
}
/* The buttons that stay are sized for a fingertip and kept on one row.
   45px was the tap target F66 measured; nothing here goes under it. */
body[data-mode="execution"][data-mobile-page="notebook"] .aios-notes-actions {
    gap: 6px; flex-wrap: wrap;
}
body[data-mode="execution"][data-mobile-page="notebook"] .aios-notes-actions .aios-icon-btn {
    padding: 8px 10px; font-size: 11px;
}

/* The close control every full-screen drawer carries. Fixed to the drawer's
   own top-right rather than placed in the flow, so it is reachable however far
   the list inside has been scrolled — the toggle that opened the drawer is
   underneath it and unreachable, which is what left the user stuck. */
.aios-drawer-close { display: none; }
body[data-mode="execution"].aios-notes-drawer-open #aios-notes-drawer-close,
body[data-mode="execution"].aios-context-drawer-open #aios-context-drawer-close {
    display: flex; align-items: center; justify-content: center;
    position: fixed; top: 8px; right: 10px; z-index: 890;
    min-width: 46px; min-height: 46px;      /* the 44px tap target, F66 */
    border: 1px solid var(--aios-border); border-radius: 10px;
    background: rgba(0,0,0,0.85); color: var(--aios-accent);
    font-size: 16px; line-height: 1; cursor: pointer;
}
/* A title on the drawer, so a full-screen takeover says what it is. */
body[data-mode="execution"].aios-notes-drawer-open #aios-notes-drawer-title,
body[data-mode="execution"].aios-context-drawer-open #aios-context-drawer-title {
    display: block; position: fixed; top: 20px; left: 14px; z-index: 890;
    color: var(--aios-accent); font-family: 'Consolas', monospace;
    font-size: 13px; letter-spacing: 1px;
}
