/* ==========================================================================
   skeuo-forms.css

   The instrument skin for the two surfaces this sheet owns: the story
   submission form (fields, consent switches, the submit key, the error
   summary) and the story plates the approved stories sit on.

   Unlayered on purpose, and loaded after skeuo-a.css, so it wins ties against
   components.css without a single !important. Shared --sk-* tokens are read
   from skeuo-a.css; hex literals appear only in the :root block below.

   Rules of the house that this file obeys:
   - a field is a recessed slot, filled slightly LIGHTER than the panel around
     it, so typed text keeps its contrast;
   - :focus-visible is an outline PLUS a glow, and no rule in this file ever
     removes an outline;
   - an error is flat: a red-tinted border, an icon and a sentence. Never a
     change of bevel, because a bevel change is invisible to most people;
   - a checked box differs from an unchecked one by the POSITION of the thumb,
     not only by its colour.
   ========================================================================== */

:root {
  /* The inside of a slot: lighter than the panel face, still darker than the
     text that will sit in it. */
  --skf-slot:      #1e2637;
  --skf-slot-lit:  #263049;
  --skf-slot-deep: #141a27;

  /* The error tint. Flat, unbevelled, deliberately off-theme. */
  --skf-bad:       #ff6b6b;
  --skf-bad-fill:  rgba(239, 68, 68, 0.10);
  --skf-bad-edge:  rgba(239, 68, 68, 0.55);

  /* The switch. A dead thumb still sits in a socket. */
  --skf-thumb:     linear-gradient(180deg, #9aa5b7 0%, #6b7688 42%, #39435a 100%);
  --skf-track-off: #10151f;
}

/* --------------------------------------------------------------------------
   The form as a panel

   The form gets the panel; the fields get slots cut into it. Nothing textured
   sits behind running text: the help lines and the legal wording are on the
   panel face, which is flat where they land.
   -------------------------------------------------------------------------- */
.story-form {
  position: relative;
  padding: var(--step-6);
  background: var(--sk-panel-face);
  border: var(--hairline) solid var(--sk-metal-deep);
  border-radius: var(--radius-card);
  box-shadow: var(--sk-bevel), var(--sk-lift);
}

@media (min-width: 48rem) {
  .story-form { padding: var(--step-7); }
}

/* Four fixings, one per corner, with two slot angles so a row of them does not
   read as a repeat. */
.story-form::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: var(--sk-screw), var(--sk-screw-b), var(--sk-screw-b), var(--sk-screw);
  background-repeat: no-repeat;
  background-size: 13px 13px;
  background-position:
    10px 10px,
    calc(100% - 10px) 10px,
    10px calc(100% - 10px),
    calc(100% - 10px) calc(100% - 10px);
}

.story-form > * { position: relative; }

/* --------------------------------------------------------------------------
   Fields as recessed slots
   -------------------------------------------------------------------------- */
.field label {
  letter-spacing: 0.01em;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}

.field input:not([type='checkbox']):not([type='radio']):not([type='file']),
.field select,
.field textarea {
  background: linear-gradient(180deg, var(--skf-slot-deep) 0, var(--skf-slot) 55%, var(--skf-slot-lit) 100%);
  border: var(--hairline) solid var(--sk-metal-deep);
  box-shadow: var(--sk-recess);
  color: var(--color-text);
  /* 16px is the size below which a phone browser zooms the page on focus and
     then leaves it zoomed. Never drop under it. */
  font-size: 1rem;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--color-text-faint); }

.field select {
  /* The native arrow is drawn by the platform and cannot be lit from the top
     left, so the control keeps one of its own. */
  appearance: none;
  padding-right: var(--step-7);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),
    linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%),
    linear-gradient(180deg, var(--skf-slot-deep) 0, var(--skf-slot) 55%, var(--skf-slot-lit) 100%);
  background-position:
    calc(100% - 20px) calc(50% + 2px),
    calc(100% - 14px) calc(50% + 2px),
    0 0;
  background-size: 6px 6px, 6px 6px, 100% 100%;
  background-repeat: no-repeat;
}

.field textarea {
  min-height: 12rem;   /* eight lines and a little air */
  line-height: var(--leading-body);
}

@supports (field-sizing: content) {
  .field textarea {
    field-sizing: content;
    min-height: calc(8lh + var(--step-4));
    max-height: 40lh;
  }
}

/* Outline AND glow. The outline is what survives a forced-colours mode and a
   high-contrast setting; the glow is what makes the slot look lit. */
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-color: var(--color-accent);
  box-shadow: var(--sk-recess), 0 0 0 4px var(--color-glow), 0 0 18px var(--color-glow-strong);
}

/* --------------------------------------------------------------------------
   The error state

   Flat, loud and plain: a solid red edge, a tinted fill, an icon and a
   sentence. No bevel moves, because a moving bevel is not a message.
   -------------------------------------------------------------------------- */
/* The :not chain is repeated from the slot rule above rather than trimmed: the
   slot rule carries three attribute selectors, and a shorter selector here
   loses the specificity contest and silently paints nothing. */
.field input[aria-invalid='true']:not([type='checkbox']):not([type='radio']):not([type='file']),
.field select[aria-invalid='true'],
.field textarea[aria-invalid='true'],
.consent input[aria-invalid='true'] {
  border-color: var(--skf-bad);
  /* The ring is drawn inside the slot so nothing shifts by a pixel when a
     field turns bad, and the recess is kept underneath it. */
  box-shadow: var(--sk-recess), inset 0 0 0 2px var(--skf-bad);
}

/* display: flex below would otherwise beat the [hidden] attribute, and an empty
   error would sit under every field showing nothing but its icon. */
.field-error[hidden] { display: none; }

.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--step-2);
  font-size: var(--size-xs);
  font-weight: 600;
  color: var(--skf-bad);
}

.field-error::before {
  content: '!';
  flex: none;
  display: grid;
  place-items: center;
  width: 1.05rem;
  height: 1.05rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  line-height: 1;
}

/* --------------------------------------------------------------------------
   The error summary

   Sits at the top of the form, is present and empty before it is ever needed,
   and is the thing focus lands on when the form is refused.
   -------------------------------------------------------------------------- */
.form-summary {
  /* Focus moves here on a refusal, and the sticky masthead would otherwise sit
     on top of the sentence explaining what went wrong. */
  scroll-margin-top: calc(var(--nav-height) + var(--step-6));
  padding: var(--step-4) var(--step-5);
  background: var(--skf-bad-fill);
  border: 2px solid var(--skf-bad-edge);
  border-radius: var(--radius-sm);
}

.form-summary:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.form-summary-title {
  display: flex;
  align-items: center;
  gap: var(--step-2);
  font-weight: 700;
  color: var(--skf-bad);
}

.form-summary-title::before {
  content: '!';
  display: grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid currentColor;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  line-height: 1;
}

.form-summary-list {
  margin: var(--step-3) 0 0;
  padding-left: var(--step-5);
  list-style: disc;
  font-size: var(--size-sm);
}

.form-summary-list li + li { margin-top: var(--step-2); }

.form-summary-list a { color: var(--color-text); }

.form-summary-list a:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* --------------------------------------------------------------------------
   The file input

   The native control never leaves the DOM and never stops being focusable: it
   is clipped away, and the styled zone is its label. Tabbing to it and pressing
   Enter opens the same file dialog it always did, and the ring below is what
   tells you that has happened.
   -------------------------------------------------------------------------- */
.file-native {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.dropzone {
  display: grid;
  place-items: center;
  gap: var(--step-2);
  padding: var(--step-6) var(--step-5);
  text-align: center;
  cursor: pointer;
  background: linear-gradient(180deg, var(--skf-slot-deep) 0, var(--skf-slot) 100%);
  border: 2px dashed var(--sk-metal-top);
  border-radius: var(--radius-sm);
  box-shadow: var(--sk-recess);
  transition: border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

.dropzone:hover { border-color: var(--color-accent); }

/* The ring belongs to the input, but the input is a clipped pixel, so the zone
   wears it on the input's behalf. */
.file-native:focus-visible + .dropzone {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-color: var(--color-accent);
  box-shadow: var(--sk-recess), 0 0 0 4px var(--color-glow);
}

.dropzone-icon {
  width: 1.5rem;
  height: 1.5rem;
  border: 2px solid var(--color-text-muted);
  border-radius: 3px;
  border-top-width: 6px;
}

.dropzone-text { font-size: var(--size-sm); font-weight: 600; color: var(--color-text); }

.dropzone-hint {
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.file-chosen {
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   Consent as chunky switches

   Still a real checkbox underneath. Checked and unchecked differ by where the
   thumb sits, so the state survives greyscale, a colour-blind reader and a
   forced-colours mode.
   -------------------------------------------------------------------------- */
.consent-set {
  margin: 0;
  padding: var(--step-5);
  border: var(--hairline) solid var(--sk-metal-deep);
  border-radius: var(--radius-card);
  background: var(--sk-plate-face);
  box-shadow: inset 0 1px 0 var(--sk-edge-lit-2), inset 0 -2px 0 var(--sk-edge-dark);
}

.consent-set > legend {
  padding: 0 var(--step-3);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
}

.consent-set .consent + .consent { margin-top: var(--step-4); }

.consent-set .consent {
  background: var(--color-surface);
  border-color: var(--sk-metal-deep);
  box-shadow: inset 0 1px 0 var(--sk-edge-lit-3);
}

.consent input[type='checkbox'] {
  appearance: none;
  flex: none;
  width: 3rem;
  height: 1.625rem;
  margin: 0.1rem 0 0;
  border-radius: var(--radius-pill);
  background: var(--skf-track-off);
  border: var(--hairline) solid var(--sk-metal-deep);
  box-shadow: var(--sk-recess);
  cursor: pointer;
  position: relative;
}

.consent input[type='checkbox']::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--skf-thumb);
  box-shadow: 0 1px 0 var(--sk-edge-lit-2), 0 2px 4px rgba(0, 0, 0, 0.75);
}

@media (prefers-reduced-motion: no-preference) {
  .consent input[type='checkbox']::after {
    transition: transform var(--dur-fast) cubic-bezier(0.2, 0.9, 0.25, 1.1);
  }
}

.consent input[type='checkbox']:checked {
  background: linear-gradient(180deg, var(--sk-led-off) 0, var(--skf-track-off) 100%);
  border-color: var(--color-accent);
}

.consent input[type='checkbox']:checked::after {
  transform: translateX(1.375rem);
}

/* A tick inside the track, so the state is not carried by position alone for a
   reader who cannot see the thumb move. */
.consent input[type='checkbox']:checked::before {
  content: '';
  position: absolute;
  top: 0.5rem;
  left: 0.55rem;
  width: 0.45rem;
  height: 0.25rem;
  border-left: 2px solid var(--color-accent);
  border-bottom: 2px solid var(--color-accent);
  transform: rotate(-45deg);
}

.consent input[type='checkbox']:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  box-shadow: var(--sk-recess), 0 0 0 4px var(--color-glow);
}

/* --------------------------------------------------------------------------
   The submit key
   -------------------------------------------------------------------------- */
.key-submit {
  width: 100%;
  min-height: 3rem;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    0 1px 2px rgba(0, 0, 0, 0.6),
    0 5px 12px rgba(0, 0, 0, 0.5),
    0 0 24px var(--color-glow);
}

@media (prefers-reduced-motion: no-preference) {
  .key-submit { transition: transform var(--dur-fast) var(--ease-out); }
}

.key-submit:active {
  transform: translateY(2px);
  box-shadow:
    inset 0 3px 7px rgba(0, 0, 0, 0.75),
    inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}

.key-submit:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

@media (min-width: 40rem) {
  .key-submit { width: auto; min-width: 14rem; }
}

/* --------------------------------------------------------------------------
   Published stories as mounted plates

   The words themselves stay on a flat slab; only the mount around them is
   physical. The attribution strip reads as the engraved label under a plate.
   -------------------------------------------------------------------------- */
.story-plate {
  position: relative;
  background: var(--sk-plate-face);
  border: var(--hairline) solid var(--sk-metal-deep);
  box-shadow: var(--sk-bevel), var(--sk-lift);
}

.story-plate::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: var(--sk-screw), var(--sk-screw-b);
  background-repeat: no-repeat;
  background-size: 11px 11px;
  background-position: 9px 9px, calc(100% - 9px) 9px;
}

/* The reading slab. Flat, no texture, no gradient: this is where the words go. */
.story-plate blockquote {
  position: relative;
  margin: 0;
  padding: var(--step-4);
  background: var(--color-page);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
  color: var(--color-text-muted);
}

.story-plate blockquote > p + p { margin-top: var(--step-4); }

.story-plate .card-title { position: relative; }

.story-plate .story-attrib {
  position: relative;
  border-top-color: var(--sk-metal-deep);
  box-shadow: 0 -1px 0 var(--sk-edge-lit-3);
}

/* --------------------------------------------------------------------------
   Forced colours

   Every boundary here has to come from a border rather than from a shadow, and
   the engraved shadows have to go, or the text doubles.
   -------------------------------------------------------------------------- */
@media (forced-colors: active) {
  .story-form,
  .story-plate,
  .consent-set,
  .consent input[type='checkbox'],
  .dropzone,
  .form-summary { border: 1px solid; box-shadow: none; }

  .story-form::before,
  .story-plate::before { display: none; }

  .field label,
  .consent-set > legend { text-shadow: none; }

  .consent input[type='checkbox']:checked::after { background: Highlight; }
}

/* --------------------------------------------------------------------------
   The narrowest screens.

   Two columns plus the plate's padding left the sentence beside the switch a
   79px column and ran it to thirteen lines. The switch takes its own row here,
   exactly as it already does on the flasher's consent.
   -------------------------------------------------------------------------- */
@media (max-width: 30rem) {
  /* Three nested plates each took a full pad, leaving the sentence a 139px
     column. The plates give their padding back to the text here. */
  .story-form { padding: var(--step-3); }
  .consent-set { padding: var(--step-3); }

  .consent-set .consent,
  .story-form .consent {
    grid-template-columns: 1fr;
    gap: var(--step-2);
    padding: var(--step-3);
  }

  .consent-set .consent input[type='checkbox'],
  .story-form .consent input[type='checkbox'] { margin-top: 0; }
}
