/*
 * What is left of the app-wide stylesheet once MudBlazor arrived (ADR-003).
 *
 * Everything that styles a control now comes from the theme in Theme/FlurfunkProTheme.cs
 * and from component-scoped .razor.css files. Three things cannot live there and stay
 * here: the loading indicator, which is painted before Blazor has started; the error
 * bar, which the framework shows by id; and the benchmark page, which deliberately runs
 * without MudBlazor so that the numbers in Planning/16 keep measuring the same thing.
 *
 * And the design tokens below, which are none of those three: they are here because a
 * component-scoped .razor.css file cannot read C#, and there are 21 of them.
 */

/* --- Design tokens ---------------------------------------------------------
 *
 * The palette of the CI handbook, the same one Theme/FlurfunkProTheme.cs builds the
 * MudBlazor theme from. That file is where a colour is decided and where the rule that
 * governs it is written down - which pairings are forbidden, what the 60-30-5
 * distribution means, why amber is never type. Read it before picking one of these.
 *
 * The brand colours are literal, because Petrol is Petrol in both schemes and a second
 * copy of a constant that never changes is harmless. The roles that do change are read
 * out of MudBlazor's own variables instead of being written twice: MudBlazor knows which
 * scheme the user actually chose - light, dark, or whatever the system says this
 * minute - and this file only ever knows what the system says. The literal after the
 * comma is not a duplicate of that value, it is the one used while no MudBlazor palette
 * exists yet, which is the second the loading indicator is painted in.
 */
:root {
    --ffp-petrol: #14505C;
    --ffp-petrol-tief: #0D3A44;
    --ffp-petrol-hell: #3C7885;
    --ffp-petrol-schrift: #003842;
    --ffp-blaugrau: #6B8C94;
    --ffp-blaugrau-hell: #8AAFB8;
    --ffp-dunst: #B8D3D9;
    --ffp-amber: #E0A458;
    --ffp-amber-tief: #B87F35;
    /* Amber as type on a light surface. Amber Tief is 3.43:1 on white and therefore
       cannot carry words at a normal size, whatever the handbook claims. */
    --ffp-amber-schrift: #95672B;
    --ffp-tinte: #1D2A2E;
    --ffp-grau: #6B7679;
    /* The secondary ink the app actually uses - Grau survives white and not Nebel, and
       one variable cannot know which of the two it has landed on. */
    --ffp-grau-tief: #677275;
    --ffp-nebel: #F2F6F7;
    --ffp-linie: #DCE5E7;
    --ffp-weiss: #FFFFFF;
    --ffp-erfolg: #2E7D5B;
    /* The status warning is Amber Tief: it is the only amber that survives on white. */
    --ffp-warnung: var(--ffp-amber-tief);
    --ffp-fehler: #A63A2E;

    --ffp-bg: var(--mud-palette-background, #FFFFFF);
    --ffp-surface: var(--mud-palette-surface, #F2F6F7);
    --ffp-fg: var(--mud-palette-text-primary, #1D2A2E);
    --ffp-muted: var(--mud-palette-text-secondary, #677275);
    --ffp-border: var(--mud-palette-divider, #DCE5E7);
    --ffp-accent: var(--mud-palette-primary, #14505C);

    /* The accent that writes words rather than filling a shape, and the one token here
       whose two values are genuinely different colours rather than the same colour at
       two lightnesses: Amber Schrift is 4.94:1 on white and 3.49:1 on the dark canvas,
       Amber is 2.2:1 on white and 7.89:1 on the dark canvas. Neither survives both, so
       the flip below is not a refinement, it is the only way either scheme passes.

       This one cannot be read out of a MudBlazor variable the way --ffp-muted is: no
       palette role holds an amber that is legible in both schemes, and MudBlazor marks
       the dark scheme with nothing in the DOM - it only swaps the variables. The shell
       therefore states the scheme itself, in MainLayout.razor, and the rule below reads
       it. Components use var(--ffp-unread) and never either amber directly. */
    --ffp-unread: var(--ffp-amber-schrift);
    /* The rail is Petrol Tief in both schemes, so this one does not flip either. */
    --ffp-sidebar-bg: var(--ffp-petrol-tief);
    --ffp-sidebar-fg: var(--ffp-dunst);

    /* 4px on controls, 8px on containers, and nothing rounder: pills are reserved for
       badges and status pips. */
    --ffp-radius: 4px;
    --ffp-radius-container: 8px;
    --ffp-sidebar-width: 275px;
    --ffp-context-width: 340px;
    /* Kept for anything that needs to line up with the bar. It is not what sets the
       bar's height - MudBlazor does that, and it offsets the clipped drawer and the main
       content from the same value, so a second number here would only be a second number
       to keep in step. Measured against the running bar rather than guessed: it is 56,
       which is also what this has said since 2.5a - it was the dense override that made
       it untrue, not the token. */
    --ffp-appbar-height: 56px;

    /* The 4/8 rhythm every gap in the feed is a multiple of. */
    --ffp-space-xs: 4px;
    --ffp-space-sm: 8px;
    --ffp-space-md: 16px;
    --ffp-space-lg: 24px;
    --ffp-space-xl: 32px;

    /* The only shadow in the system, and only for what floats: menus, popovers, dialogs.
       Mixed from Tinte rather than black, so it reads as ink rather than as dusk. */
    --ffp-shadow-overlay: 0 1px 3px rgba(29, 42, 46, 0.08), 0 4px 12px rgba(29, 42, 46, 0.05);
}

/* Dark. Only the pre-boot values change - after the first render the lines above already
   follow the theme the user picked, and the system preference is the only thing that can
   be known before then. The values match PaletteDark in Theme/FlurfunkProTheme.cs. */
@media (prefers-color-scheme: dark) {
    :root {
        --ffp-bg: var(--mud-palette-background, #101D21);
        --ffp-surface: var(--mud-palette-surface, #16262B);
        --ffp-fg: var(--mud-palette-text-primary, #E5F3F8);
        --ffp-muted: var(--mud-palette-text-secondary, #B8D3D9);
        --ffp-border: var(--mud-palette-divider, #2B3E44);
        --ffp-accent: var(--mud-palette-primary, #99CFDD);
    }
}

/* The scheme the app is actually in, which is not the same question as the one the media
   query above asks: somebody on a light desktop can pick Dark in the user menu, and from
   that moment the OS preference is the wrong answer. MainLayout writes the attribute from
   ThemePreference.IsDark, which is the only place that knows.

   It is here rather than in MessageList.razor.css because a scoped stylesheet cannot
   define a variable for a surface it does not own, and because the next component that
   needs an amber should find the token already made. */
.ffp-shell[data-scheme="dark"] {
    --ffp-unread: var(--ffp-amber);
}

html, body {
    margin: 0;
    padding: 0;
}

/* The first thing the keyboard reaches, and invisible until it does: without it every
   visit starts with a walk through the whole sidebar (N11, WCAG 2.4.1). */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 2000;
    padding: 0.6rem 1rem;
    background: var(--ffp-petrol);
    color: var(--ffp-weiss);
    border-radius: 0 0 var(--ffp-radius) 0;
}

.skip-link:focus {
    left: 0;
}

/* Text for the screen reader and for nobody else.
 *
 * Not display:none and not visibility:hidden - both take the element out of the
 * accessibility tree as well, which is the opposite of the point. The 1px clip is the
 * old trick because it is the one that survives everything: the text stays in the tree,
 * stays in the reading order, and takes up no room.
 *
 * Global rather than scoped: three components need it now (the composer's attach
 * control, the user menu's activator, the quick switcher's rows), and a scoped copy in
 * each would be three rules that have to agree by hand.
 */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* A visible focus ring everywhere, including where a component library would rather
   not have one (WCAG 2.4.7). A crisp ring in the brand colour and no outer glow, which
   is what the handbook asks for; on dark the token is the lightened Petrol, because a
   ring has to be seen against the surface it sits on rather than match a swatch. */
:focus-visible {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}

.loading-progress {
    display: block;
    width: 6rem;
    height: 6rem;
    margin: 20vh auto 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: var(--ffp-accent);
    stroke-width: 0.4rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--ffp-accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress circle:first-child {
    stroke: var(--ffp-muted);
    opacity: 0.25;
}

.loading-progress-text {
    text-align: center;
    color: var(--ffp-muted);
    font-size: 0.875rem;
}

.loading-progress-text::after {
    content: "Loading " var(--blazor-load-percentage-text, "");
}

#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    padding: 0.75rem 1.25rem;
    /* The one bar the framework paints itself, so it cannot come from the theme.
       Amber is the handbook's signal colour and Tinte the ink that survives on it at
       6.8:1; the border replaces the drop shadow the design system does not allow. */
    background: var(--ffp-amber);
    color: var(--ffp-tinte);
    border-top: 1px solid var(--ffp-amber-tief);
    /* Above everything MudBlazor draws, and that is the whole point of the number.
       It used to be 1000, which is under the drawer at 1100 - so the one bar in the
       application that says "this window is broken, reload it" was covered by the
       sidebar, with only the tail of the word Reload sticking out past its edge. The
       framework's own scale ends at the tooltip on 1600; this sits clear of it, because
       there is nothing a person needs to see less than a tooltip over a message telling
       them the app has stopped working. */
    z-index: 2000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: right;
}

/* --- Phase 0 forms -------------------------------------------------------- */

.field {
    margin-bottom: 1rem;
}

.field label {
    display: block;
    margin-bottom: 0.25rem;
    font-weight: 600;
    color: var(--ffp-fg);
}

.field input {
    width: 100%;
    max-width: 24rem;
    padding: 0.5rem 0.625rem;
    border: 1px solid color-mix(in srgb, var(--ffp-muted) 45%, transparent);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    color: var(--ffp-fg);
    font: inherit;
}

.field input:focus-visible,
button:focus-visible {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}

button {
    padding: 0.5rem 1rem;
    border: 0;
    border-radius: var(--ffp-radius);
    background: var(--ffp-accent);
    color: #fff;
    font: inherit;
    cursor: pointer;
}

button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

button.link {
    padding: 0;
    background: none;
    color: var(--ffp-accent);
    text-decoration: underline;
}

.error {
    color: #b42318;
    font-weight: 600;
}

dl {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.25rem 1rem;
    margin: 0 0 1rem;
}

dt {
    font-weight: 600;
}

dd {
    margin: 0;
    color: var(--ffp-muted);
    font-variant-numeric: tabular-nums;
}

h2 {
    margin-top: 2rem;
    font-size: 1.125rem;
}

.validation-message {
    display: block;
    margin-top: 0.25rem;
    color: #b42318;
    font-size: 0.875rem;
}

/* --- Message list benchmark ----------------------------------------------- */

.ffp-bare {
    max-width: 60rem;
    margin: 0 auto;
    padding: 2rem 1.5rem;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

.benchmark {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    height: 100vh;
    max-width: 60rem;
    margin: 0 auto;
    padding: 1.5rem 1rem;
    box-sizing: border-box;
}

.benchmark header h1 {
    margin: 0 0 0.25rem;
}

.benchmark header p {
    margin: 0;
}

.benchmark .controls {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.benchmark table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

.benchmark th,
.benchmark td {
    padding: 0.375rem 0.5rem;
    text-align: left;
    border-bottom: 1px solid color-mix(in srgb, var(--ffp-muted) 25%, transparent);
}

.benchmark td:nth-child(2),
.benchmark td:nth-child(3) {
    font-variant-numeric: tabular-nums;
}

.benchmark .overall {
    margin: 0.75rem 0 0;
    padding: 0.625rem 0.875rem;
    border-radius: 6px;
    font-weight: 600;
}

.benchmark .overall.pass {
    background: color-mix(in srgb, #2b8a3e 18%, transparent);
    color: #2b8a3e;
}

.benchmark .overall.fail {
    background: color-mix(in srgb, #b42318 18%, transparent);
    color: #b42318;
}

.benchmark .small {
    font-size: 0.8125rem;
}

/* The list needs a bounded height, otherwise it grows and virtualisation has nothing
   to virtualise against. */
.benchmark .list-frame {
    flex: 1;
    min-height: 20rem;
    border: 1px solid color-mix(in srgb, var(--ffp-muted) 30%, transparent);
    border-radius: 8px;
    overflow: hidden;
}

/* ---------------------------------------------------------------------------
   What a rendered message looks like (#161, #165).

   Every class here is one RenderedContent.cs hands out. They were all undefined
   until 22.09.2026 except the table: the renderer named its blocks from the
   start and nothing ever styled them, so a message fell back on whatever
   MudBlazor does to a bare <p>, <ul> or <pre>. Nothing looked broken, which is
   why it lasted a design phase - an undefined class is not an error in a
   browser, it is a nothing.

   The values are the ones the Flutter client already reasoned its way to
   (mobile/lib/src/features/messages/rendered_content.dart), because the two
   clients draw the same tree and a message should be the same message in both.
   Where that file explains a choice, the choice is repeated here rather than
   re-decided.
   --------------------------------------------------------------------------- */

/* The 4px DESIGN.md puts between paragraphs of the same sender - above every
   block but the first, so a one-paragraph message carries no stray space. */
.ffp-md-p,
.ffp-md-h,
.ffp-md-quote,
.ffp-md-list,
.ffp-md-pre,
.ffp-md-rule,
.ffp-md-card,
.ffp-md-table-scroll {
    margin: var(--ffp-space-xs) 0 0;
}

.ffp-md-p:first-child,
.ffp-md-h:first-child,
.ffp-md-quote:first-child,
.ffp-md-list:first-child,
.ffp-md-pre:first-child,
.ffp-md-rule:first-child,
.ffp-md-card:first-child,
.ffp-md-table-scroll:first-child {
    margin-top: 0;
}

.ffp-md-p {
    /* Inherited rather than set: a thread pane and a quoted preview draw the same
       tree smaller, and a size written here would override the one they chose. */
    line-height: 1.45;
}

/* A heading in a message is not a page title. The renderer already draws a div
   with an aria-level two steps down for that reason; this is the visual half -
   larger and in the serif, never the weight of an h1 in a timeline. The scale is
   Flutter's: 1.30, 1.20, 1.12, then body size with a serif face. */
.ffp-md-h {
    font-family: var(--ffp-font-serif);
    font-weight: 700;
    line-height: 1.25;
}

.ffp-md-h1 { font-size: 1.3em; }
.ffp-md-h2 { font-size: 1.2em; }
.ffp-md-h3 { font-size: 1.12em; }
.ffp-md-h4,
.ffp-md-h5,
.ffp-md-h6 { font-size: 1em; }

/* A Slack attachment (#65). The same left-edge idea as a quotation - the content
   came from somewhere else - but with the sender's own colour on it, because on an
   alert the colour is the message: green and red are what a person reads first and
   the words second.

   The accent arrives as --ffp-card-accent and falls back to the border grey when the
   sender named none, so a card without a colour is still a card and not a stray
   indent. The property is set on the element by the renderer and nowhere else. */
.ffp-md-card {
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    border-left: 3px solid var(--ffp-card-accent, var(--ffp-border));
    border-radius: 0 var(--ffp-radius) var(--ffp-radius) 0;
    background: var(--ffp-surface);
}

/* Cards stack. Without this two attachments touch and read as one. */
.ffp-md-card + .ffp-md-card {
    margin-top: var(--ffp-space-sm);
}

/* The heading of a card is its title, and it sits tighter to the text under it
   than a heading in running prose does. */
.ffp-md-card > .ffp-md-h {
    margin-bottom: 2px;
}

/* The 3px Petrol edge DESIGN.md gives the AI-assist block, reused for a
   quotation: both say "this text came from somewhere else". */
.ffp-md-quote {
    padding-left: var(--ffp-space-sm);
    border-left: 3px solid var(--ffp-accent);
    color: var(--ffp-muted);
}

/* A marker column wide enough that the second line of a wrapped item aligns
   under the first word rather than under the bullet. */
.ffp-md-list {
    padding-left: var(--ffp-space-xl);
}

.ffp-md-list li + li {
    margin-top: var(--ffp-space-xs);
}

.ffp-md-list .ffp-md-list {
    margin-top: var(--ffp-space-xs);
}

/* Scrollable rather than wrapped: a wrapped line of code is a line of code that
   has changed meaning, and in a column this narrow almost every line would wrap. */
.ffp-md-pre {
    padding: var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    overflow-x: auto;
}

.ffp-md-pre code {
    font-family: ui-monospace, Menlo, "Courier New", monospace;
    font-size: 0.9em;
    line-height: 1.4;
    /* `language` is recorded on the node and deliberately not highlighted: a
       highlighter is a dependency and a payload, and #94 watches that number. */
    white-space: pre;
}

/* Tight, not a padded pill. Inline code sits between words and has to keep their
   baseline and their line height; the Flutter side paints exactly the glyph box
   for the same reason. */
.ffp-md-code {
    padding: 0 0.2em;
    border-radius: 2px;
    background: var(--ffp-surface);
    font-family: ui-monospace, Menlo, "Courier New", monospace;
    font-size: 0.9em;
}

.ffp-md-rule {
    margin-bottom: var(--ffp-space-xs);
    border: 0;
    border-top: 1px solid var(--ffp-border);
}

/* A mention, as a small tinted pill.
 *
 * Amber for "this is about you", Petrol for everyone else's. The handbook caps
 * Amber at one anchor per view and a mention of the reader is what it reserves
 * that anchor for; a mention of a third party is information, not a signal.
 *
 * Never Amber as type - the handbook forbids it outright, which is why the tint
 * is on the ground and the letters stay ink. */
.ffp-mention {
    padding: 1px var(--ffp-space-xs);
    border-radius: var(--ffp-radius);
    background: color-mix(in srgb, var(--ffp-accent) 12%, transparent);
    color: var(--ffp-accent);
    font-weight: 600;
}

.ffp-mention-me {
    background: color-mix(in srgb, var(--ffp-amber) 28%, transparent);
    color: var(--ffp-fg);
}

/* ---------------------------------------------------------------------------
   Tables inside a rendered message (#161).
   --------------------------------------------------------------------------- */

.ffp-md-table-scroll {
    /* The scroller, not the table. A wide table has to move on its own; make the
       message column scroll and every other message slides sideways with it. */
    max-width: 100%;
    overflow-x: auto;
    /* The spacing comes from the shared block rule above now, so that a table keeps
       the same rhythm as the paragraphs around it rather than its own. */
}

.ffp-md-table {
    border-collapse: collapse;
    font-size: 0.875rem;
    /* Not width:100%. A two-column table stretched across the timeline puts an
       arm's length between the label and its number. */
    width: auto;
}

.ffp-md-table th,
.ffp-md-table td {
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    /* The author's alignment arrives as an inline style and wins over this. */
    text-align: left;
    vertical-align: top;
}

.ffp-md-table th {
    background: var(--ffp-surface);
    font-weight: 600;
}
