Compare commits
35
Commits
+22
-1
@@ -4,7 +4,28 @@ One line per change, grouped by the release that shipped it. Newest
|
|||||||
first. A release is a Cargo.toml bump plus a `v<version>` tag; the
|
first. A release is a Cargo.toml bump plus a `v<version>` tag; the
|
||||||
content repos pin the tag they run (see the end of this file).
|
content repos pin the tag they run (see the end of this file).
|
||||||
|
|
||||||
## Unreleased
|
## 0.6.15 (2026-10-01)
|
||||||
|
|
||||||
|
- An invite may carry its own mail: `invite: { group, mail: { subject, body } }`. Every field of the form is a placeholder besides `{name}`, `{link}`, `{expires}` and the rest, and a date and time (`type: datetime-local`) is written out in words ("tirsdag 6. oktober kl. 15:00"). The words and the form are kept with the invite, so sending it again sends the same mail. This is for a desk that invites someone to a conversation at an hour, not into a group for good.
|
||||||
|
|
||||||
|
## 0.6.14 (2026-10-01)
|
||||||
|
|
||||||
|
- Less room above the wordmark: 1.75rem, 1.25rem on a narrow screen (it was 4rem). The space was kept for who is here, which now sits on the wordmark's own line, and on a tall, narrow screen it read as dead space.
|
||||||
|
|
||||||
|
## 0.6.13 (2026-10-01)
|
||||||
|
|
||||||
|
- Who is here (the name and "sign out", or "sign in") sits on the wordmark's own line instead of being pinned to the corner of the page, where it read as a row of its own. With a picture strip it is in the wordmark's bar; without, it is level with the mark at the right edge. On a narrow screen it drops under the mark.
|
||||||
|
- Trainer: every form, public ones included, carries a signed-in person's name and address from the login instead of asking for them; a visitor still sees the fields. Only the inside forms did before, so someone signed in filled in their own name and address again on a public form.
|
||||||
|
|
||||||
|
## 0.6.12 (2026-10-01)
|
||||||
|
|
||||||
|
- Trainer: the words model is told who opens each mail: the person who sent the case, the person who posted the record an answer is about, or someone at the business. Mails to the business about another person's case used to be written as if to that person ("you will hear from us"); on Gullberg, three of nine were.
|
||||||
|
|
||||||
|
## 0.6.11 (2026-10-01)
|
||||||
|
|
||||||
|
- `wordmark_ink: true` in `site.yaml`: an SVG wordmark is drawn in the page's own ink, as a mask over the text colour, so it follows the page's light and dark in every browser. A wordmark shown as an `<img>` only knows the system's setting, and some browsers don't pass even that on. Opacity in the file still shows, so a lighter line stays lighter. Opt-in, for one-colour marks; a mark with a colour of its own keeps its colours.
|
||||||
|
|
||||||
|
## 0.6.10 (2026-09-30)
|
||||||
|
|
||||||
- Trainer: who a business is gets a page of its own ("who are we?", `/hvem` in Norwegian), with nothing to do on it. It has one card per need that `presents` (the people, the work, each shown as picture cards) and one for the plan's `values`. The front door leads there instead of carrying it all.
|
- Trainer: who a business is gets a page of its own ("who are we?", `/hvem` in Norwegian), with nothing to do on it. It has one card per need that `presents` (the people, the work, each shown as picture cards) and one for the plan's `values`. The front door leads there instead of carrying it all.
|
||||||
- Trainer: the words model is told that a form page from the inside asks a real question and says where the answer ends up ("How do you present yourself?", not "Your card on the front page"), and that a `one_per_person` form is the colleague's own.
|
- Trainer: the words model is told that a form page from the inside asks a real question and says where the answer ends up ("How do you present yourself?", not "Your card on the front page"), and that a `one_per_person` form is the colleague's own.
|
||||||
|
|||||||
Generated
+1
-1
@@ -3357,7 +3357,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portal"
|
name = "portal"
|
||||||
version = "0.6.9"
|
version = "0.6.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "portal"
|
name = "portal"
|
||||||
version = "0.6.9"
|
version = "0.6.15"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ on every hot reload, and in CI through `iris check`.
|
|||||||
```yaml
|
```yaml
|
||||||
title: Vestmark kommune # the browser title and the header
|
title: Vestmark kommune # the browser title and the header
|
||||||
wordmark: /site/logo.svg # an image instead of the title
|
wordmark: /site/logo.svg # an image instead of the title
|
||||||
|
wordmark_ink: true # a one-colour SVG mark drawn in the page's own ink
|
||||||
wordmark_invert: true # flip it in dark mode
|
wordmark_invert: true # flip it in dark mode
|
||||||
wordmark_height: 2.4 # rem
|
wordmark_height: 2.4 # rem
|
||||||
lang: no # the interface language: en or no
|
lang: no # the interface language: en or no
|
||||||
@@ -124,6 +125,10 @@ answer:
|
|||||||
from: [open, incomplete] # defaults to the initial state alone
|
from: [open, incomplete] # defaults to the initial state alone
|
||||||
label: Send them
|
label: Send them
|
||||||
invite: { group: case_officers } # onboard someone into Kanidm
|
invite: { group: case_officers } # onboard someone into Kanidm
|
||||||
|
# or with words of its own, using any field of the form:
|
||||||
|
# invite:
|
||||||
|
# group: guests
|
||||||
|
# mail: { subject: "...", body: "... {tidspunkt} ... {link} ..." }
|
||||||
```
|
```
|
||||||
|
|
||||||
`self_transition.from` is what keeps a person in dialogue with their
|
`self_transition.from` is what keeps a person in dialogue with their
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 409.87222 77.398003" role="img" aria-label="GULLBERG sanatorium">
|
||||||
|
<title>GULLBERG sanatorium</title>
|
||||||
|
<!-- Futura, drawn as outlines so every machine shows the same mark.
|
||||||
|
"sanatorium" is spaced out to run the width of GULLBERG, set in
|
||||||
|
a little from each edge. As an image it colours itself for light
|
||||||
|
and dark; with wordmark_ink in site.yaml, portal colours it with
|
||||||
|
the page's own ink instead, and "sanatorium" stays lighter by its
|
||||||
|
opacity. -->
|
||||||
|
<style>
|
||||||
|
.mark { fill: #1d1b18; }
|
||||||
|
.sub { fill: #1d1b18; fill-opacity: 0.6; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.mark, .sub { fill: #f1ece3; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<path class="mark"
|
||||||
|
d="M 0,25.216 Q 0,19.84 1.984,15.232 3.968,10.624 7.36,7.232 10.816,3.84 15.424,1.92 20.032,0 25.216,0 q 2.88,0 5.44,0.576 2.624,0.512 4.992,1.728 2.432,1.216 4.736,3.072 2.304,1.856 4.608,4.48 l -5.184,4.928 Q 33.856,6.912 25.344,6.912 q -3.84,0 -7.168,1.408 -3.264,1.408 -5.696,3.904 -2.432,2.432 -3.84,5.76 -1.344,3.328 -1.344,7.232 0,3.968 1.472,7.296 1.536,3.328 3.968,5.76 2.496,2.368 5.632,3.712 3.136,1.344 6.4,1.344 3.008,0 5.568,-0.96 2.56,-1.024 4.416,-2.688 1.92,-1.664 3.072,-3.904 1.152,-2.304 1.408,-4.8 V 30.912 H 27.2 v -6.848 h 19.968 v 1.6 q 0,5.504 -1.6,10.048 -1.6,4.48 -4.544,7.68 -2.88,3.2 -7.104,4.992 -4.16,1.728 -9.344,1.728 -5.12,0 -9.6,-1.92 Q 10.496,46.208 7.168,42.816 3.84,39.424 1.92,34.88 0,30.336 0,25.216 Z M 69.360026,0.96 h 7.296004 v 30.208 q 0,3.328 0.832,5.696 0.832,2.304 2.24,3.712 1.472,1.408 3.392,2.048 1.92,0.64 4.096,0.64 2.176,0 4.096,-0.64 1.92,-0.64 3.328,-2.048 1.472,-1.472 2.304,-3.84 0.896,-2.368 0.896,-5.824 V 0.96 h 7.232 V 32 q 0,4.352 -1.472,7.744 -1.408,3.392 -3.84,5.696 -2.432,2.304 -5.696,3.52 -3.2,1.152 -6.848,1.152 -3.648,0 -6.912,-1.152 -3.2,-1.216 -5.632004,-3.52 -2.432,-2.304 -3.904,-5.696 -1.408,-3.392 -1.408,-7.744 z M 129.18404,49.216 V 0.96 h 7.296 v 41.408 h 14.208 v 6.848 z m 41.90405,0 V 0.96 h 7.296 v 41.408 h 14.208 v 6.848 z m 41.90405,0 V 0.96 h 8.32 q 8.064,0 12.48,3.264 4.48,3.2 4.48,9.792 0,3.2 -1.536,5.76 -1.472,2.496 -3.904,3.84 v 0.064 q 4.416,1.28 6.912,4.416 2.56,3.136 2.56,7.296 0,3.52 -1.28,6.144 -1.28,2.56 -3.584,4.288 -2.304,1.728 -5.568,2.56 -3.2,0.832 -7.168,0.832 z m 12.416,-6.848 q 2.496,0 4.288,-0.576 1.792,-0.576 2.944,-1.536 1.152,-0.96 1.664,-2.24 0.512,-1.344 0.512,-2.88 0,-1.536 -0.512,-2.816 -0.512,-1.344 -1.728,-2.368 -1.152,-1.024 -3.136,-1.6 -1.92,-0.576 -4.8,-0.576 h -4.352 v 14.592 z m -2.688,-20.672 q 4.352,0 6.336,-1.984 1.984,-1.984 1.984,-5.504 0,-3.456 -2.048,-4.992 -1.984,-1.536 -6.272,-1.536 h -2.432 V 21.696 Z M 291.31219,0.96 v 6.848 h -19.328 v 11.584 h 18.816 v 6.848 h -18.816 v 16.128 h 19.328 v 6.848 h -26.624 V 0.96 Z m 32.56003,0 q 4.16,0 7.552,0.832 3.392,0.832 5.824,2.56 2.432,1.728 3.776,4.416 1.344,2.624 1.344,6.336 0,4.736 -2.688,8.32 -2.624,3.52 -7.808,5.376 l 14.72,20.416 h -8.896 l -13.568,-19.776 h -1.536 v 19.776 h -7.296 V 0.96 Z m -1.28,22.144 h 2.304 q 5.12,0 7.744,-1.984 2.624,-1.984 2.624,-5.888 0,-7.424 -10.048,-7.424 h -2.624 z m 40.112,2.112 q 0,-5.376 1.984,-9.984 1.984,-4.608 5.376,-8 3.456,-3.392 8.064,-5.312 4.608,-1.92 9.792,-1.92 2.88,0 5.44,0.576 2.624,0.512 4.992,1.728 2.432,1.216 4.736,3.072 2.304,1.856 4.608,4.48 l -5.184,4.928 q -5.952,-7.872 -14.464,-7.872 -3.84,0 -7.168,1.408 -3.264,1.408 -5.696,3.904 -2.432,2.432 -3.84,5.76 -1.344,3.328 -1.344,7.232 0,3.968 1.472,7.296 1.536,3.328 3.968,5.76 2.496,2.368 5.632,3.712 3.136,1.344 6.4,1.344 3.008,0 5.568,-0.96 2.56,-1.024 4.416,-2.688 1.92,-1.664 3.072,-3.904 1.152,-2.304 1.408,-4.8 v -0.064 h -12.032 v -6.848 h 19.968 v 1.6 q 0,5.504 -1.6,10.048 -1.6,4.48 -4.544,7.68 -2.88,3.2 -7.104,4.992 -4.16,1.728 -9.344,1.728 -5.12,0 -9.6,-1.92 -4.48,-1.984 -7.808,-5.376 -3.328,-3.392 -5.248,-7.936 -1.92,-4.544 -1.92,-9.664 z"
|
||||||
|
id="g"
|
||||||
|
|
||||||
|
aria-label="GULLBERG" />
|
||||||
|
<path class="sub"
|
||||||
|
d="m 11.999999,76.176 1.079,-0.819 q 0.195,0.338 0.481,0.546 0.286,0.195 0.676,0.195 0.351,0 0.546,-0.143 0.195,-0.156 0.195,-0.442 0,-0.143 -0.065,-0.26 -0.052,-0.117 -0.195,-0.234 -0.143,-0.117 -0.39,-0.247 -0.247,-0.13 -0.624,-0.299 -0.793,-0.364 -1.118,-0.793 -0.325,-0.442 -0.325,-1.066 0,-0.403 0.156,-0.728 0.156,-0.325 0.416,-0.546 0.273,-0.234 0.624,-0.351 0.364,-0.13 0.78,-0.13 0.676,0 1.17,0.247 0.507,0.247 0.793,0.663 l -1.053,0.845 q -0.143,-0.182 -0.403,-0.325 -0.247,-0.143 -0.52,-0.143 -0.273,0 -0.429,0.117 -0.156,0.117 -0.156,0.312 0,0.104 0.052,0.195 0.052,0.091 0.195,0.195 0.143,0.104 0.39,0.234 0.26,0.13 0.663,0.312 0.819,0.377 1.118,0.832 0.299,0.442 0.299,1.066 0,0.455 -0.156,0.832 -0.156,0.364 -0.442,0.624 -0.286,0.26 -0.676,0.39 -0.39,0.143 -0.845,0.143 -0.884,0 -1.443,-0.377 -0.559,-0.377 -0.793,-0.845 z m 40.993985,-2.067 q 0,-0.689 0.221,-1.274 0.234,-0.598 0.637,-1.027 0.416,-0.442 0.962,-0.689 0.559,-0.247 1.209,-0.247 0.585,0 1.079,0.195 0.494,0.195 0.962,0.65 h 0.013 v -0.689 h 1.43 v 6.188 h -1.43 v -0.689 h -0.013 q -0.468,0.455 -0.962,0.663 -0.481,0.208 -1.04,0.208 -0.663,0 -1.235,-0.247 -0.559,-0.26 -0.975,-0.702 -0.403,-0.442 -0.637,-1.04 -0.221,-0.598 -0.221,-1.3 z m 1.456,0 q 0,0.429 0.117,0.806 0.13,0.364 0.364,0.637 0.234,0.273 0.572,0.429 0.351,0.143 0.78,0.143 0.429,0 0.767,-0.143 0.351,-0.156 0.598,-0.416 0.247,-0.273 0.377,-0.637 0.143,-0.364 0.143,-0.793 0,-0.416 -0.143,-0.78 -0.13,-0.364 -0.377,-0.624 -0.247,-0.273 -0.598,-0.429 -0.338,-0.156 -0.754,-0.156 -0.416,0 -0.767,0.156 -0.338,0.156 -0.585,0.416 -0.234,0.26 -0.364,0.624 -0.13,0.351 -0.13,0.767 z M 98.512,71.028 v 0.663 h 0.026 q 0.338,-0.39 0.78,-0.611 0.455,-0.221 1.092,-0.221 1.079,0 1.599,0.676 0.533,0.663 0.533,1.885 v 3.796 h -1.43 v -3.458 q 0,-0.897 -0.286,-1.248 -0.286,-0.364 -0.884,-0.364 -0.663,0 -1.053,0.442 -0.377,0.442 -0.377,1.521 v 3.107 h -1.43 v -6.188 z m 41.05899,3.081 q 0,-0.689 0.221,-1.274 0.234,-0.598 0.637,-1.027 0.416,-0.442 0.962,-0.689 0.559,-0.247 1.209,-0.247 0.585,0 1.079,0.195 0.494,0.195 0.962,0.65 h 0.013 v -0.689 h 1.43 v 6.188 h -1.43 v -0.689 h -0.013 q -0.468,0.455 -0.962,0.663 -0.481,0.208 -1.04,0.208 -0.663,0 -1.235,-0.247 -0.559,-0.26 -0.975,-0.702 -0.403,-0.442 -0.637,-1.04 -0.221,-0.598 -0.221,-1.3 z m 1.456,0 q 0,0.429 0.117,0.806 0.13,0.364 0.364,0.637 0.234,0.273 0.572,0.429 0.351,0.143 0.78,0.143 0.429,0 0.767,-0.143 0.351,-0.156 0.598,-0.416 0.247,-0.273 0.377,-0.637 0.143,-0.364 0.143,-0.793 0,-0.416 -0.143,-0.78 -0.13,-0.364 -0.377,-0.624 -0.247,-0.273 -0.598,-0.429 -0.338,-0.156 -0.754,-0.156 -0.416,0 -0.767,0.156 -0.338,0.156 -0.585,0.416 -0.234,0.26 -0.364,0.624 -0.13,0.351 -0.13,0.767 z M 183.958,71.028 v -2.262 h 1.43 v 2.262 h 1.105 v 1.339 h -1.105 v 4.849 h -1.43 v -4.849 h -1.001 v -1.339 z m 38.849,3.055 q 0,-0.663 0.234,-1.248 0.234,-0.585 0.663,-1.014 0.442,-0.442 1.04,-0.702 0.611,-0.26 1.352,-0.26 0.741,0 1.339,0.26 0.611,0.247 1.04,0.689 0.442,0.442 0.676,1.04 0.247,0.598 0.247,1.287 0,0.689 -0.247,1.287 -0.234,0.598 -0.676,1.04 -0.429,0.429 -1.04,0.689 -0.611,0.247 -1.365,0.247 -0.728,0 -1.339,-0.247 -0.598,-0.26 -1.027,-0.702 -0.429,-0.442 -0.663,-1.053 -0.234,-0.611 -0.234,-1.313 z m 1.456,0.026 q 0,0.923 0.494,1.469 0.494,0.546 1.339,0.546 0.832,0 1.339,-0.546 0.507,-0.546 0.507,-1.443 0,-0.884 -0.507,-1.43 -0.494,-0.559 -1.339,-0.559 -0.403,0 -0.741,0.143 -0.338,0.143 -0.585,0.416 -0.247,0.26 -0.377,0.624 -0.13,0.351 -0.13,0.78 z m 42.28101,-3.081 h 1.43 v 0.559 h 0.026 q 0.351,-0.377 0.65,-0.546 0.312,-0.182 0.767,-0.182 0.559,0 1.17,0.377 l -0.65,1.3 q -0.403,-0.286 -0.793,-0.286 -1.17,0 -1.17,1.755 v 3.211 h -1.43 z m 42.15098,0 v 6.188 h -1.43 v -6.188 z m -1.651,-2.691 q 0,-0.377 0.273,-0.65 0.273,-0.273 0.65,-0.273 0.39,0 0.663,0.273 0.286,0.273 0.286,0.65 0,0.377 -0.273,0.663 -0.273,0.273 -0.663,0.273 -0.403,0 -0.676,-0.273 -0.26,-0.286 -0.26,-0.663 z m 39.122,2.691 h 1.43 v 3.549 q 0,1.547 1.209,1.547 1.222,0 1.222,-1.547 v -3.549 h 1.43 v 3.588 q 0,1.313 -0.676,2.054 -0.676,0.728 -1.976,0.728 -0.598,0 -1.092,-0.169 -0.481,-0.169 -0.832,-0.507 -0.338,-0.351 -0.533,-0.871 -0.182,-0.52 -0.182,-1.235 z m 42.74902,0 h 1.43 V 71.6 h 0.013 q 0.286,-0.338 0.65,-0.533 0.377,-0.208 0.962,-0.208 0.624,0 1.027,0.234 0.403,0.221 0.702,0.689 h 0.013 q 0.377,-0.455 0.845,-0.689 0.481,-0.234 1.118,-0.234 1.105,0 1.651,0.637 0.546,0.637 0.546,1.898 v 3.822 h -1.43 v -3.432 q 0,-0.897 -0.26,-1.261 -0.26,-0.377 -0.845,-0.377 -0.65,0 -0.949,0.429 -0.286,0.416 -0.286,1.313 v 3.328 h -1.43 v -3.523 q 0,-0.806 -0.26,-1.17 -0.26,-0.377 -0.819,-0.377 -0.637,0 -0.949,0.429 -0.299,0.416 -0.299,1.313 v 3.328 h -1.43 z"
|
||||||
|
id="s"
|
||||||
|
|
||||||
|
aria-label="sanatorium" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.5 KiB |
@@ -0,0 +1,61 @@
|
|||||||
|
# Aksjonæravtalen for Gullberg AS: punkter å dekke
|
||||||
|
|
||||||
|
Ikke en avtale, og ikke på siden. En liste over det aksjonæravtalen bør
|
||||||
|
svare på, til samtale mellom de fire og med advokat eller regnskapsfører.
|
||||||
|
Lånet går over lang tid (rundt 100 000 kr i året mot 1,6 millioner), så
|
||||||
|
avtalen må tåle at livene til de fire endrer seg underveis.
|
||||||
|
|
||||||
|
## Eierskapet
|
||||||
|
|
||||||
|
- Hvem de fire aksjonærene er, med 25 % hver, og hva hver har skutt inn.
|
||||||
|
- Hvordan huset går fra dødsboet til selskapet: skjøte, takst som legges
|
||||||
|
til grunn, og hvem som betaler dokumentavgiften.
|
||||||
|
- Hva søsknene som løses ut får, når, og at de ikke har noe krav etterpå.
|
||||||
|
|
||||||
|
## Lånet
|
||||||
|
|
||||||
|
- Hvem som står for lånet overfor banken, og om noen har kausjonert
|
||||||
|
personlig. Hvis bare noen kausjonerer: hvordan de holdes skadesløse av
|
||||||
|
de andre.
|
||||||
|
- Hva som skjer hvis husleia ikke dekker avdraget en måned, eller et
|
||||||
|
halvår: hvem skyter inn, i hvilket forhold, og som lån eller kapital.
|
||||||
|
- Hva som skjer hvis renta stiger mye.
|
||||||
|
|
||||||
|
## Driften
|
||||||
|
|
||||||
|
- Hva styret avgjør alene, og hva som krever alle fire (nye lån, salg av
|
||||||
|
huset, store utbedringer over et beløp, endring av husleia, ny eier).
|
||||||
|
- Hva som skjer ved stemmelikhet, to mot to: hvem eller hva som bryter.
|
||||||
|
- Klingenberg Bygg som forvalter, og som den som gir overslag og gjør
|
||||||
|
arbeidet: hvordan overslag godkjennes, og hvordan en mulig
|
||||||
|
interessekonflikt håndteres.
|
||||||
|
- Et vedlikeholdsfond: hvor mye av overskuddet som settes av før noe
|
||||||
|
deles ut.
|
||||||
|
|
||||||
|
## Bruken av huset
|
||||||
|
|
||||||
|
- Hvordan sommerukene fordeles mellom de fire, hvem som velger først, og
|
||||||
|
hva som skjer med uker ingen bruker.
|
||||||
|
- Om uker kan leies ut til andre, og hvem som får inntekten.
|
||||||
|
- Hva aksjonærene betaler for egne uker, om noe.
|
||||||
|
|
||||||
|
## Når noe endrer seg
|
||||||
|
|
||||||
|
- En aksjonær vil selge: forkjøpsrett for de andre, hvordan prisen
|
||||||
|
fastsettes (takst, formel), og hvor lang tid de andre har.
|
||||||
|
- En aksjonær dør eller skiller seg: om aksjene går til arvinger eller
|
||||||
|
ektefelle, eller om de andre kan løse dem ut.
|
||||||
|
- En aksjonær klarer ikke sin del: hva som skjer med andelen.
|
||||||
|
- Huset skal selges: hvordan, og hvordan pengene fordeles etter lånet.
|
||||||
|
- Brann, stor skade, eller at huset ikke kan leies ut: forsikring, og
|
||||||
|
hva som skjer med lånet.
|
||||||
|
|
||||||
|
## Utbytte og innsyn
|
||||||
|
|
||||||
|
- Når det deles ut utbytte, og i hvilken rekkefølge (lån, fond, utbytte).
|
||||||
|
- Hva hver aksjonær har rett til å se (rapportene på siden, regnskapet).
|
||||||
|
|
||||||
|
## Tvister og varighet
|
||||||
|
|
||||||
|
- Hvordan uenighet løses før noen går til retten (mekling).
|
||||||
|
- Når avtalen tas opp igjen, for eksempel hvert femte år.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Huset
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: hva huset er, hvor det ligger, hvor mange rom det har og i hvilken stand.
|
||||||
|
|
||||||
|
# Forretningen
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: de to inntektene, romleie til studenter og timeshare-uker, og hva hver av dem gir i året.
|
||||||
|
|
||||||
|
# Eierne
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: Gullberg AS eies av fire aksjonærer med 25 % hver. Hvem de er, hvordan styret er satt sammen, og hva som krever alle fire.
|
||||||
|
|
||||||
|
# Lånet
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: 1,6 millioner til å løse ut de to søsknene som ikke blir med (800 000 kr hver), og at selskapet overtar huset ved skjøte tinglyst på selskapet. Dokumentavgiften (normalt 2,5 % av verdien), rente, løpetid og sikkerhet slik banken tilbyr, og hvordan avdraget betjenes av husleia.
|
||||||
|
|
||||||
|
# Budsjettet
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: inntekter, drift, renter og avdrag for et normalår, og hvor rapportene står.
|
||||||
|
|
||||||
|
# Det som mangler
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: de største manglene ved huset, hva de koster å rette, og planen for det. Lista på siden holdes oppdatert.
|
||||||
|
|
||||||
|
# Forvaltningen
|
||||||
|
|
||||||
|
Utkast, skrives av eierne: at Klingenberg Bygg forvalter huset, og hva det innebærer.
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
# GULLBERG sanatorium - et Klingenberg Bygg-prosjekt på
|
||||||
|
# gullberg.klingenbergbygg.no. UTKAST 2026-09-30, til samtale; ikke
|
||||||
|
# bygget eller satt ut.
|
||||||
|
#
|
||||||
|
# Oppsettet (samtale 2026-10-01): hele huset legges i Gullberg AS, som
|
||||||
|
# eies av fire aksjonærer med 25 % hver. Selskapet låner 1,6 millioner i
|
||||||
|
# banken for å løse ut de to søsknene som ikke blir med (800 000 kr hver),
|
||||||
|
# og overtar huset (skjøte tinglyst på selskapet). Dokumentavgiften,
|
||||||
|
# normalt 2,5 % av verdien, må med i lånet eller budsjettet. Rundt
|
||||||
|
# 100 000 kr i året til banken er en lang nedbetaling: alt som kan gå
|
||||||
|
# galt underveis hører hjemme i aksjonæravtalen (se
|
||||||
|
# documents/gullberg-aksjonaeravtale-punkter.md, ikke på siden).
|
||||||
|
#
|
||||||
|
# Huset ligger bak Ås kirke, i Syverudveien (17?), et kort stykke fra
|
||||||
|
# NMBU. Rommene er for alle, men studenter går foran.
|
||||||
|
#
|
||||||
|
# Siden er huset i drift: rom til leie, feil meldt til Klingenberg
|
||||||
|
# Bygg som forvalter, styrets liste over det som er bra, mangler og er
|
||||||
|
# planlagt, husleia måned for måned, kvartalsrapporter, nedbetalingsplanen
|
||||||
|
# alle fire står bak, og eierskiftet steg for steg. Banken er ikke en egen
|
||||||
|
# gruppe på siden ennå; styret sender rapportene.
|
||||||
|
#
|
||||||
|
# Pengene: 3 soverom à 8 000 kr er 24 000 kr i måneden, rundt 264 000 i
|
||||||
|
# året over 11 måneder; sommeren er timeshare-uker. Husleia går som fast
|
||||||
|
# trekk eller AvtaleGiro til selskapets konto; Vipps eller Stripe bare for
|
||||||
|
# engangsbeløp. Depositum på egen depositumskonto (husleieloven).
|
||||||
|
#
|
||||||
|
# Åpne spørsmål:
|
||||||
|
# - hvem de fire aksjonærene er, og om nye medeiere søkes ved siden av
|
||||||
|
# lånet (medeier-tilbudet er tatt ut av forsiden til det er avgjort)
|
||||||
|
# - bankens tilbud: rente, løpetid, sikkerhet, kausjon
|
||||||
|
# - takst; hvem som sitter i styret
|
||||||
|
# - antall måneder studentene leier; timeshare bare for aksjonærene?
|
||||||
|
# - regnskapsfører/advokat: skiftet, dokumentavgift, skatt på husleie
|
||||||
|
# - kontakt og avsender (under er plassholdere)
|
||||||
|
business: GULLBERG sanatorium
|
||||||
|
language: no
|
||||||
|
# Plassholder til styret har en adresse.
|
||||||
|
contact: { name: Styret i Gullberg, email: bl@klingenbergbygg.no, group: styret }
|
||||||
|
people: memberships
|
||||||
|
mail: { from: "Gullberg <gullberg@klingenbergbygg.no>", reply_to: bl@klingenbergbygg.no }
|
||||||
|
about: >
|
||||||
|
Et gammelt hus bak Ås kirke, i Syverudveien, med rimelige rom til leie;
|
||||||
|
studenter ved NMBU går foran, men alle kan søke,
|
||||||
|
og uker om sommeren for dem som eier det. Drevet av eierne, forvaltet
|
||||||
|
av Klingenberg Bygg; det som mangler, står på lista med planen for
|
||||||
|
det, og tallene ligger åpent.
|
||||||
|
|
||||||
|
groups:
|
||||||
|
- { id: styret, label: Styret }
|
||||||
|
- { id: aksjonaerer, label: Aksjonærer }
|
||||||
|
- { id: leietakere, label: Leietakere }
|
||||||
|
- { id: klingenberg, label: Klingenberg Bygg }
|
||||||
|
invites_by: [styret]
|
||||||
|
|
||||||
|
does:
|
||||||
|
- id: rom
|
||||||
|
for: leie-rom
|
||||||
|
text: >
|
||||||
|
Rimelige rom bak Ås kirke, et kort stykke fra NMBU. Studenter går
|
||||||
|
foran, men alle kan søke. Du søker her, kommer på visning og sier
|
||||||
|
selv ja til tilbudet.
|
||||||
|
|
||||||
|
# Forretningsplanen, ord for ord: ett kort per del. Skal skrives av
|
||||||
|
# eierne; filen har bare delene og hva som hører hjemme i dem.
|
||||||
|
documents:
|
||||||
|
- id: forretningsplan
|
||||||
|
question: Hvordan går Gullberg rundt?
|
||||||
|
description: Forretningsplanen for Gullberg, slik eierne har lagt den fram for banken.
|
||||||
|
file: documents/gullberg-forretningsplan.md
|
||||||
|
|
||||||
|
needs:
|
||||||
|
# Offentlig: noen søker rom. Studenter går foran; styret ser hva den
|
||||||
|
# som søker gjør til daglig, og velger etter det.
|
||||||
|
- id: leie-rom
|
||||||
|
who: en som trenger et rimelig rom i Ås, oftest en student ved NMBU
|
||||||
|
wants: et rom å bo på til en leie som går opp, med studenter først i køen
|
||||||
|
lands_in: romsoknader
|
||||||
|
handled_by: styret
|
||||||
|
stages: [open, visning, tilbud, akseptert, signert, avslatt]
|
||||||
|
stages_text: { open: "mottatt", visning: "visning avtalt", tilbud: "tilbud sendt", akseptert: "tilbud godtatt", signert: "kontrakt signert", avslatt: "avsluttet uten leie" }
|
||||||
|
done_when: [signert, avslatt]
|
||||||
|
success: [signert]
|
||||||
|
turned_down_from: [open, visning, tilbud]
|
||||||
|
moves_by: { akseptert: [submitter], avslatt: [submitter, styret] }
|
||||||
|
mails: { open: submitter, visning: submitter, tilbud: submitter, akseptert: bl@klingenbergbygg.no, signert: submitter }
|
||||||
|
grants: { signert: leietakere }
|
||||||
|
max_fields: 4
|
||||||
|
collects:
|
||||||
|
- { name: name }
|
||||||
|
- { name: email, type: email }
|
||||||
|
- { name: telefon, type: tel }
|
||||||
|
- { name: situasjon, type: select, options: [Student ved NMBU, Student et annet sted, Jobber eller annet] }
|
||||||
|
- { name: innflytting, type: date, optional: true }
|
||||||
|
- { name: om, type: textarea, optional: true }
|
||||||
|
|
||||||
|
# Rommene som er ledige, som bildekort styret legger inn og tar ned.
|
||||||
|
- id: ledig-rom
|
||||||
|
who: styret, når et rom blir ledig
|
||||||
|
wants: at de som søker ser rommet før de søker
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: rom
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [public]
|
||||||
|
presents: true
|
||||||
|
public_states: [open]
|
||||||
|
stages: [open, utleid]
|
||||||
|
stages_text: { open: "ledig", utleid: "leid ut" }
|
||||||
|
done_when: [utleid]
|
||||||
|
success: [utleid]
|
||||||
|
leaves_after: { utleid: { minutes: 10 } }
|
||||||
|
max_fields: 3
|
||||||
|
collects:
|
||||||
|
- { name: tittel }
|
||||||
|
- { name: bilde, type: picture }
|
||||||
|
- { name: om, type: textarea }
|
||||||
|
|
||||||
|
# Styrets liste: det som er bra, det som mangler, det som er planlagt.
|
||||||
|
# Aksjonærene ser alt; allmennheten bare planene styret har publisert,
|
||||||
|
# og de står som kunngjøringer.
|
||||||
|
- id: liste
|
||||||
|
who: styret, om huset og driften
|
||||||
|
wants: én liste over det som er bra, det som mangler og det som er planlagt, med pris og tid
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: liste
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer, public]
|
||||||
|
public_states: [publisert]
|
||||||
|
stages: [open, vedtatt, publisert, forkastet]
|
||||||
|
stages_text: { open: "utkast", vedtatt: "vedtatt", publisert: "publisert", forkastet: "forkastet" }
|
||||||
|
done_when: [publisert, forkastet]
|
||||||
|
success: [publisert]
|
||||||
|
max_fields: 3
|
||||||
|
collects:
|
||||||
|
- { name: tittel }
|
||||||
|
- { name: slag, type: select, options: [Kvalitet, Mangel, Plan] }
|
||||||
|
- { name: beskrivelse, type: textarea }
|
||||||
|
- { name: kostnad, optional: true }
|
||||||
|
- { name: naar, optional: true }
|
||||||
|
|
||||||
|
# Budsjettrapporten: tallene i felt, ikke bare en PDF. Aksjonærene ser
|
||||||
|
# den her; styret sender den til banken og fører inn at den er sendt.
|
||||||
|
- id: rapport
|
||||||
|
who: styret, hvert kvartal
|
||||||
|
wants: at aksjonærene ser hvordan det går, i tall, og at banken får det den skal ha
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: rapporter
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer]
|
||||||
|
stages: [open, sendt]
|
||||||
|
stages_text: { open: "lagt fram for aksjonærene", sendt: "sendt til banken" }
|
||||||
|
done_when: [sendt]
|
||||||
|
success: [sendt]
|
||||||
|
max_fields: 7
|
||||||
|
collects:
|
||||||
|
- { name: periode }
|
||||||
|
- { name: leieinntekter }
|
||||||
|
- { name: timeshare }
|
||||||
|
- { name: driftskostnader }
|
||||||
|
- { name: renter-og-avdrag }
|
||||||
|
- { name: saldo }
|
||||||
|
- { name: kommentar, type: textarea }
|
||||||
|
- { name: dokument, type: file, optional: true }
|
||||||
|
|
||||||
|
# Nedbetalingsplanen: hvordan pengene går og hva banken får. Styret
|
||||||
|
# legger den fram, BEGGE aksjonærene må godkjenne før den går til
|
||||||
|
# banken, og styret fører inn bankens svar. "Alle i gruppa" finnes ikke
|
||||||
|
# i portal ennå: i dag holder det at én trykker. Ny portal-funksjon før
|
||||||
|
# dette brukes.
|
||||||
|
- id: nedbetalingsplan
|
||||||
|
who: styret, når veien videre og pengestrømmen skal avtales
|
||||||
|
wants: en nedbetalingsplan alle fire aksjonærene står bak, som banken sier ja til
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: nedbetalingsplaner
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer]
|
||||||
|
stages: [open, godkjent, sendt, innvilget, endringer]
|
||||||
|
stages_text: { open: "til godkjenning hos aksjonærene", godkjent: "godkjent av aksjonærene", sendt: "sendt til banken", innvilget: "innvilget av banken", endringer: "banken ber om endringer" }
|
||||||
|
reached_from: { godkjent: [open], sendt: [godkjent], innvilget: [sendt], endringer: [sendt] }
|
||||||
|
done_when: [innvilget, endringer]
|
||||||
|
success: [innvilget]
|
||||||
|
moves_by: { godkjent: [aksjonaerer], sendt: [styret], innvilget: [styret], endringer: [styret] }
|
||||||
|
max_fields: 6
|
||||||
|
collects:
|
||||||
|
- { name: tittel }
|
||||||
|
- { name: laanebelop }
|
||||||
|
- { name: maanedlig-avdrag }
|
||||||
|
- { name: forste-trekk, type: date }
|
||||||
|
- { name: pengestrom, type: textarea }
|
||||||
|
- { name: dokument, type: file, optional: true }
|
||||||
|
|
||||||
|
# Husleia, måned for måned: forventet mot betalt, fra kontoutskriften.
|
||||||
|
# Aksjonærene og långiveren ser hvordan det går mellom rapportene.
|
||||||
|
- id: husleie
|
||||||
|
who: styret, når husleia for en måned er kommet inn
|
||||||
|
wants: at aksjonærene ser hvilke rom som har betalt, og hva som gikk til banken
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: husleie
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer]
|
||||||
|
stages: [open, betalt, purret]
|
||||||
|
stages_text: { open: "forventet", betalt: "betalt", purret: "purret" }
|
||||||
|
reached_from: { purret: [open], betalt: [open, purret] }
|
||||||
|
done_when: [betalt]
|
||||||
|
success: [betalt]
|
||||||
|
max_fields: 4
|
||||||
|
collects:
|
||||||
|
- { name: rom }
|
||||||
|
- { name: maaned }
|
||||||
|
- { name: belop }
|
||||||
|
- { name: betalt-dato, type: date, optional: true }
|
||||||
|
|
||||||
|
# Feil i huset: en leietaker melder fra, Klingenberg Bygg ser på det og
|
||||||
|
# gir et overslag, styret godkjenner kostnaden før arbeidet gjøres.
|
||||||
|
- id: feil
|
||||||
|
who: en leietaker som har funnet noe som er galt i huset
|
||||||
|
wants: at det blir sett på og rettet
|
||||||
|
enters_as: leietakere
|
||||||
|
lands_in: feil
|
||||||
|
handled_by: klingenberg
|
||||||
|
# Leietakeren ser sine egne meldinger og hvor langt de har kommet.
|
||||||
|
followed_by: [leietakere]
|
||||||
|
watched_by: [styret]
|
||||||
|
stages: [open, befaring, overslag, godkjent, utbedret, avvist]
|
||||||
|
stages_text: { open: "meldt", befaring: "befaring avtalt", overslag: "overslag gitt", godkjent: "godkjent av styret", utbedret: "utbedret", avvist: "avvist" }
|
||||||
|
reached_from: { avvist: [open, befaring, overslag] }
|
||||||
|
done_when: [utbedret, avvist]
|
||||||
|
success: [utbedret]
|
||||||
|
moves_by: { godkjent: [styret], avvist: [styret, klingenberg] }
|
||||||
|
mails: { open: fredrik@klingenbergbygg.no, overslag: bl@klingenbergbygg.no, utbedret: submitter }
|
||||||
|
max_fields: 3
|
||||||
|
collects:
|
||||||
|
- { name: hva }
|
||||||
|
- { name: hvor, optional: true }
|
||||||
|
- { name: beskrivelse, type: textarea }
|
||||||
|
- { name: email, type: email }
|
||||||
|
- { name: bilde, type: picture, optional: true }
|
||||||
|
|
||||||
|
# Overslaget: Klingenberg Bygg gir et beløp for å rette en feil. Styret
|
||||||
|
# og aksjonærene ser det; godkjente overslag hører med i rapporten.
|
||||||
|
- id: overslag
|
||||||
|
who: Klingenberg Bygg, når en feil er sett på
|
||||||
|
wants: at styret får et beløp å ta stilling til, og at det havner i regnskapet
|
||||||
|
enters_as: klingenberg
|
||||||
|
lands_in: overslag
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer, klingenberg]
|
||||||
|
stages: [open, godkjent, avvist, fakturert]
|
||||||
|
stages_text: { open: "til styret", godkjent: "godkjent", avvist: "avvist", fakturert: "fakturert" }
|
||||||
|
reached_from: { avvist: [open], fakturert: [godkjent] }
|
||||||
|
done_when: [fakturert, avvist]
|
||||||
|
success: [fakturert]
|
||||||
|
moves_by: { fakturert: [klingenberg] }
|
||||||
|
mails: { open: bl@klingenbergbygg.no }
|
||||||
|
max_fields: 3
|
||||||
|
collects:
|
||||||
|
- { name: gjelder }
|
||||||
|
- { name: belop }
|
||||||
|
- { name: hva-det-dekker, type: textarea }
|
||||||
|
- { name: dokument, type: file, optional: true }
|
||||||
|
|
||||||
|
# Eierskiftet, steg for steg: fra selskapet stiftes til søsknene er
|
||||||
|
# utbetalt. Aksjonærene og banken ser hvor langt det har kommet.
|
||||||
|
- id: eierskifte
|
||||||
|
who: styret, mens huset går over til selskapet
|
||||||
|
wants: at aksjonærene og banken ser hvor langt eierskiftet har kommet, med dokumentene
|
||||||
|
enters_as: styret
|
||||||
|
lands_in: eierskifte
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer]
|
||||||
|
stages: [open, arbeid, ferdig]
|
||||||
|
stages_text: { open: "ikke startet", arbeid: "i gang", ferdig: "ferdig" }
|
||||||
|
done_when: [ferdig]
|
||||||
|
success: [ferdig]
|
||||||
|
max_fields: 2
|
||||||
|
collects:
|
||||||
|
- { name: steg, type: select, options: [Selskapet stiftet, Takst, Lånet innvilget, Aksjonæravtale signert, Skjøte signert, Skjøte tinglyst, Dokumentavgift betalt, Søsknene utbetalt] }
|
||||||
|
- { name: merknad, type: textarea, optional: true }
|
||||||
|
- { name: dokument, type: file, optional: true }
|
||||||
|
|
||||||
|
# Aksjonærene: uker i huset, og saker til styret.
|
||||||
|
- id: uke
|
||||||
|
who: en aksjonær som vil bruke huset en periode
|
||||||
|
wants: en uke i huset, bekreftet av styret
|
||||||
|
enters_as: aksjonaerer
|
||||||
|
lands_in: uker
|
||||||
|
handled_by: styret
|
||||||
|
stages: [open, bekreftet, avslatt]
|
||||||
|
stages_text: { open: "ønsket", bekreftet: "bekreftet", avslatt: "ikke ledig" }
|
||||||
|
done_when: [bekreftet, avslatt]
|
||||||
|
success: [bekreftet]
|
||||||
|
max_fields: 3
|
||||||
|
collects:
|
||||||
|
- { name: fra, type: date }
|
||||||
|
- { name: til, type: date }
|
||||||
|
- { name: merknad, type: textarea, optional: true }
|
||||||
|
|
||||||
|
- id: sak
|
||||||
|
who: en aksjonær som vil ha noe avgjort
|
||||||
|
wants: at styret tar saken opp og avgjør den
|
||||||
|
enters_as: aksjonaerer
|
||||||
|
lands_in: saker
|
||||||
|
handled_by: styret
|
||||||
|
watched_by: [aksjonaerer]
|
||||||
|
stages: [open, sakslisten, vedtatt, avvist]
|
||||||
|
stages_text: { open: "meldt", sakslisten: "på sakslisten", vedtatt: "vedtatt", avvist: "avvist" }
|
||||||
|
done_when: [vedtatt, avvist]
|
||||||
|
success: [vedtatt]
|
||||||
|
max_fields: 2
|
||||||
|
collects:
|
||||||
|
- { name: tittel }
|
||||||
|
- { name: sak, type: textarea }
|
||||||
|
|
||||||
|
personas:
|
||||||
|
- { id: student-nmbu, need: leie-rom, text: "Jeg begynner på master på NMBU til høsten og har ikke råd til hybel i Ås sentrum. Et rimelig rom i et stort hus høres perfekt ut." }
|
||||||
|
- { id: ung-arbeider, need: leie-rom, text: "Jeg har fått jobb i Ås og trenger et rimelig sted å bo. Jeg er ikke student; kan jeg søke likevel?" }
|
||||||
|
- { id: student-kort, need: leie-rom, text: "Jeg skal på utveksling og trenger et rom bare for ett semester. Går det an?" }
|
||||||
|
- { id: styret-rom, need: ledig-rom, text: "Rommet i andre etasje blir ledig til jul, og jeg har et fint bilde av det. Det vil jeg legge ut." }
|
||||||
|
- { id: styret-mangel, need: liste, text: "Taket på uthuset lekker. Det må stå på lista, og vi må ha en plan og en pris for det." }
|
||||||
|
- { id: styret-kvartal, need: rapport, text: "Tredje kvartal er gjort opp. Leia kom inn, men strømmen ble dyrere enn vi trodde. Rapporten skal til banken." }
|
||||||
|
- { id: leietaker-vann, need: feil, text: "Det drypper fra taket på badet i andre etasje når noen dusjer over. Hvem sier jeg fra til?" }
|
||||||
|
- { id: klingenberg-overslag, need: overslag, text: "Vi har sett på lekkasjen på badet. Det er en pakning og litt flis, rundt 6 500 kroner. Styret må si ja før vi setter i gang." }
|
||||||
|
- { id: styret-skjote, need: eierskifte, text: "Skjøtet er tinglyst på selskapet i dag. Banken skal se det, og kopien skal ligge ved." }
|
||||||
|
- { id: aksjonaer-sommer, need: uke, text: "Vi vil gjerne være i huset første uka i juli med barna." }
|
||||||
|
- { id: styret-plan, need: nedbetalingsplan, text: "Vi har regnet på det: 24 000 i måneden fra rommene, og vi kan sette av en fast sum til långiveren hver måned. Alle fire må stå bak det før vi går til banken." }
|
||||||
|
- { id: styret-husleie, need: husleie, text: "Oktober er inne for to av rommene, det tredje mangler. Det må stå et sted alle ser." }
|
||||||
|
- { id: aksjonaer-sak, need: sak, text: "Jeg mener vi bør se på solceller før vi tar opp mer lån. Kan styret ta det opp?" }
|
||||||
@@ -289,7 +289,7 @@ slideshow:
|
|||||||
- image: images/bildeserie/20.webp
|
- image: images/bildeserie/20.webp
|
||||||
width: 1867
|
width: 1867
|
||||||
height: 1400
|
height: 1400
|
||||||
caption: Her heises Moelvens modulskole i Vestby på plass. Den blir stående i 10 år.
|
caption: Her heises Moelvens modulskole i Vestby på plass. Den blir stående i minst 10 år.
|
||||||
- image: images/bildeserie/21.webp
|
- image: images/bildeserie/21.webp
|
||||||
width: 1867
|
width: 1867
|
||||||
height: 1400
|
height: 1400
|
||||||
@@ -309,4 +309,4 @@ slideshow:
|
|||||||
- image: images/bildeserie/25.webp
|
- image: images/bildeserie/25.webp
|
||||||
width: 1487
|
width: 1487
|
||||||
height: 1400
|
height: 1400
|
||||||
caption: pH-test av borprøver i mur fra forskjellige dybder
|
caption: pH-test av borprøver i betong fra forskjellige dybder
|
||||||
|
|||||||
@@ -0,0 +1,985 @@
|
|||||||
|
{
|
||||||
|
"aggregates.yaml": {
|
||||||
|
"feil": {
|
||||||
|
"open": {
|
||||||
|
"subject": "Ny feil meldt på Gullberg: {hva}",
|
||||||
|
"body": "En leietaker på Gullberg har meldt fra om {hva}. Se meldingen og avtal befaring: {link}"
|
||||||
|
},
|
||||||
|
"overslag": {
|
||||||
|
"subject": "Overslag til styret: {hva}",
|
||||||
|
"body": "Klingenberg Bygg har sett på {hva} og gitt et overslag. Styret godkjenner eller avviser det før arbeidet settes i gang: {link}"
|
||||||
|
},
|
||||||
|
"utbedret": {
|
||||||
|
"subject": "{hva} er rettet",
|
||||||
|
"body": "Feilen du meldte om, {hva}, er nå utbedret. Si gjerne til hvis noe fortsatt ikke er som det skal. {link}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overslag": {
|
||||||
|
"open": {
|
||||||
|
"subject": "Nytt overslag fra Klingenberg Bygg: {gjelder}",
|
||||||
|
"body": "Klingenberg Bygg har sendt et overslag for {gjelder}. Det ligger hos styret til godkjenning: {link}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"romsoknader": {
|
||||||
|
"akseptert": {
|
||||||
|
"subject": "{name} har takket ja til rommet",
|
||||||
|
"body": "{name} har takket ja til tilbudet om rom på Gullberg. Neste steg er kontrakten og depositumet. {link}"
|
||||||
|
},
|
||||||
|
"open": {
|
||||||
|
"subject": "Søknaden din om rom på Gullberg er mottatt",
|
||||||
|
"body": "Hei {name},\n\nsøknaden din om rom på Gullberg er mottatt. Styret går gjennom søknadene fortløpende; studenter går foran, men alle blir sett på. Du hører fra oss om visning. {link}\n\nDet du kan gjøre nå: {site}/decide/leie-rom?chain={chain}&email={email}\n\nHilsen styret i Gullberg"
|
||||||
|
},
|
||||||
|
"signert": {
|
||||||
|
"subject": "Kontrakten er signert",
|
||||||
|
"body": "Kontrakten for rommet du søkte om er nå signert av begge parter. Rommet er ditt - velkommen inn. {link}"
|
||||||
|
},
|
||||||
|
"tilbud": {
|
||||||
|
"subject": "Du har fått tilbud om rom",
|
||||||
|
"body": "Styret har sett på søknaden din og kan tilby deg rommet. Svar her så snart du kan, så går vi videre med kontrakten. {link}\n\nDet du kan gjøre nå: {site}/decide/leie-rom?chain={chain}&email={email}"
|
||||||
|
},
|
||||||
|
"visning": {
|
||||||
|
"subject": "Visning av rommet er satt opp",
|
||||||
|
"body": "Søknaden din har kommet til visning - styret vil vise deg rommet før det avgjøres. Du hører fra oss om tid og sted. {link}\n\nDet du kan gjøre nå: {site}/decide/leie-rom?chain={chain}&email={email}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/aksjonaerer/index.yaml": {
|
||||||
|
"question": "Hvordan ligger huset og driften an?",
|
||||||
|
"description": "Her ser aksjonærene hvordan huset og driften står: eierskiftet, husleia, lista, overslagene og sakene.",
|
||||||
|
"confirm": "Bekreft",
|
||||||
|
"buckets": {
|
||||||
|
"eierskifte": {
|
||||||
|
"heading": "Se hvor langt eierskiftet har kommet",
|
||||||
|
"description": "Hvert steg i overgangen til Gullberg AS, med dokumentene som hører til.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Steg i eierskiftet",
|
||||||
|
"list_note": "Hver rad er et steg med merknad og dokument; flytt det videre når det er i arbeid eller ferdig."
|
||||||
|
},
|
||||||
|
"husleie": {
|
||||||
|
"heading": "Se hvilke rom som har betalt",
|
||||||
|
"description": "Husleia for hvert rom, måned for måned, og hva som er purret på.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Husleie",
|
||||||
|
"list_note": "Hver rad er ett rom for én måned, med beløp og betalingsdato; merk den betalt eller purret."
|
||||||
|
},
|
||||||
|
"liste": {
|
||||||
|
"heading": "Se det som er bra, mangler og planlagt",
|
||||||
|
"description": "Én liste over tilstanden i huset, med pris og tid på det som står igjen.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Lista",
|
||||||
|
"list_note": "Hver rad er ett punkt med kostnad og tidspunkt; vedta den, publiser den eller forkast den."
|
||||||
|
},
|
||||||
|
"nedbetalingsplaner": {
|
||||||
|
"heading": "Godkjenn nedbetalingsplanen",
|
||||||
|
"description": "Planen for lånet, lagt fram for alle fire aksjonærene og banken.",
|
||||||
|
"moves": {
|
||||||
|
"open->godkjent": "Godkjenn planen"
|
||||||
|
},
|
||||||
|
"list_title": "Nedbetalingsplaner",
|
||||||
|
"list_note": "Hver rad er en plan med lånebeløp og pengestrøm; følg den fra godkjent til innvilget hos banken."
|
||||||
|
},
|
||||||
|
"overslag": {
|
||||||
|
"heading": "Se overslagene fra Klingenberg Bygg",
|
||||||
|
"description": "Beløpet Klingenberg Bygg har satt på en feil, klart til vedtak.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Overslag",
|
||||||
|
"list_note": "Hver rad er ett overslag med beløp og hva det dekker; godkjenn, avvis eller fakturer det."
|
||||||
|
},
|
||||||
|
"rapporter": {
|
||||||
|
"heading": "Se kvartalsrapportene",
|
||||||
|
"description": "Tallene aksjonærene og banken skal ha, kvartal for kvartal.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Rapporter",
|
||||||
|
"list_note": "Hver rad er én periode med inntekter, kostnader og saldo; send den når den er klar."
|
||||||
|
},
|
||||||
|
"saker": {
|
||||||
|
"heading": "Se sakene som er meldt til styret",
|
||||||
|
"description": "Det en aksjonær vil ha styrets ord på, samlet på ett sted.",
|
||||||
|
"moves": {},
|
||||||
|
"list_title": "Saksliste",
|
||||||
|
"list_note": "Hver rad er én sak; sett den på sakslisten, vedta den eller avvis den."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"uke": {
|
||||||
|
"heading": "Be om en uke i huset",
|
||||||
|
"description": "Reserver en periode selv, bekreftet av styret.",
|
||||||
|
"button": "Søk om uke"
|
||||||
|
},
|
||||||
|
"sak": {
|
||||||
|
"heading": "Få noe avgjort av styret",
|
||||||
|
"description": "Meld en sak du vil ha styrets ord på.",
|
||||||
|
"button": "Meld sak"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invites": {
|
||||||
|
"aksjonaerer": {
|
||||||
|
"heading": "Inviter en ny aksjonær inn",
|
||||||
|
"description": "De får en konto og en lenke som gjelder én gang.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Ny aksjonær",
|
||||||
|
"form_description": "Ha navn, e-post og telefon klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invited": {
|
||||||
|
"heading": "Se hvem som er invitert",
|
||||||
|
"description": "Status på invitasjonene som er sendt ut.",
|
||||||
|
"done": "Aktiv",
|
||||||
|
"withdraw": "Trekk tilbake",
|
||||||
|
"again": "Send på nytt",
|
||||||
|
"list_title": "Invitasjoner",
|
||||||
|
"list_note": "Hver rad er én invitasjon; send den på nytt om lenken har gått ut, eller trekk den tilbake."
|
||||||
|
},
|
||||||
|
"suggestions": null
|
||||||
|
},
|
||||||
|
"questions/aksjonaerer/sak-sent.yaml": {
|
||||||
|
"question": "Hva skjer med saken jeg nettopp sendte inn?",
|
||||||
|
"description": "Saken er sendt til styret. Den havner på sakslisten og blir tatt opp der.",
|
||||||
|
"next_heading": "Følg saken videre",
|
||||||
|
"next_description": "Styret vurderer saken og vedtar eller avviser den. Du finner status på sakslisten."
|
||||||
|
},
|
||||||
|
"questions/aksjonaerer/sak.yaml": {
|
||||||
|
"heading": "Få en sak opp i styret",
|
||||||
|
"description": "Noe som bør avgjøres, men ikke er ditt å avgjøre alene? Legg den fram, så tar styret den opp.",
|
||||||
|
"button": "Send saken",
|
||||||
|
"encouragement": "Styret ser den og setter den på sakslisten.",
|
||||||
|
"icon": "vote",
|
||||||
|
"paused": "Pause mottak av saker",
|
||||||
|
"requires_title": "Krev tittel",
|
||||||
|
"requires_description": "Krev beskrivelse av saken",
|
||||||
|
"requires_icon": "Krev ikon",
|
||||||
|
"road_title": "Fra forslag til vedtak",
|
||||||
|
"road_description": "Saken kommer inn og settes på sakslisten, blir tatt opp av styret og ender med et vedtak eller en avvisning.",
|
||||||
|
"road_icon": "list",
|
||||||
|
"who_title": "Hvem som leser den",
|
||||||
|
"who_description": "Styret får saken og tar stilling til den; haster det, nå dem på bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Ny sak",
|
||||||
|
"form_description": "Skriv kort hva saken går ut på og hvorfor den bør opp.",
|
||||||
|
"labels": {
|
||||||
|
"tittel": "Tittel på saken",
|
||||||
|
"sak": "Hva gjelder saken"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"tittel": "Fast eller flytende leie for sommeren",
|
||||||
|
"sak": "Vi bør avklare om timeshare-ukene skal prises likt hele året, eller om sommeren skal koste mer. Foreslår at styret tar stilling til dette før neste sesong."
|
||||||
|
},
|
||||||
|
"question": "Er det noe som bør avgjøres av styret?",
|
||||||
|
"page_description": "Her melder du inn en sak du vil at styret skal ta opp og avgjøre."
|
||||||
|
},
|
||||||
|
"questions/aksjonaerer/uke-sent.yaml": {
|
||||||
|
"question": "Hva skjer nå med ukesøknaden din?",
|
||||||
|
"description": "Den er sendt og ligger hos styret, som avgjør om uken blir bekreftet.",
|
||||||
|
"next_heading": "Styret ser på søknaden din",
|
||||||
|
"next_description": "De bekrefter eller avslår uken. Du finner svaret i oversikten når det er klart."
|
||||||
|
},
|
||||||
|
"questions/aksjonaerer/uke.yaml": {
|
||||||
|
"heading": "Reserver en uke i huset",
|
||||||
|
"description": "Si fra når du vil bruke huset, så ser styret om datoene passer.",
|
||||||
|
"button": "Send ønske",
|
||||||
|
"encouragement": "Du hører fra styret så snart datoene er sjekket.",
|
||||||
|
"icon": "calendar",
|
||||||
|
"paused": "Tar vi ikke imot ukeønsker nå",
|
||||||
|
"requires_title": "Krever tittel",
|
||||||
|
"requires_description": "Krever beskrivelse",
|
||||||
|
"requires_icon": "Krever ikon",
|
||||||
|
"road_title": "Veien videre",
|
||||||
|
"road_description": "Styret ser på ønsket ditt og bekrefter eller avslår uken.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem leser dette",
|
||||||
|
"who_description": "Styret ser ønsket ditt; skriv til bl@klingenbergbygg.no om det haster.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Ukeønske",
|
||||||
|
"form_description": "Fyll inn datoene du ønsker huset.",
|
||||||
|
"labels": {
|
||||||
|
"fra": "Fra dato",
|
||||||
|
"til": "Til dato",
|
||||||
|
"merknad": "Merknad"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"fra": "f.eks. 6. juli 2027",
|
||||||
|
"til": "f.eks. 13. juli 2027",
|
||||||
|
"merknad": "f.eks. kommer med familie, trenger ekstra seng"
|
||||||
|
},
|
||||||
|
"question": "Hvilken uke vil du bruke huset?",
|
||||||
|
"page_description": "Meld fra om uken du ønsker, så bekrefter styret den."
|
||||||
|
},
|
||||||
|
"questions/decide/leie-rom.yaml": {
|
||||||
|
"question": "Vil du ta imot tilbudet om rom?",
|
||||||
|
"description": "Styret har sett på søknaden din og tilbyr deg et rom. Si ja og det er ditt, si nei og det går videre til neste på lista.",
|
||||||
|
"moves": {
|
||||||
|
"akseptert": {
|
||||||
|
"heading": "Ta imot rommet",
|
||||||
|
"description": "Du sier ja til rommet du har fått tilbud om. Styret tar kontakt om innflytting og signering.",
|
||||||
|
"button": "Ta imot"
|
||||||
|
},
|
||||||
|
"avslatt": {
|
||||||
|
"heading": "Takk nei til rommet",
|
||||||
|
"description": "Du sier nei til tilbudet, og rommet går videre til neste søker.",
|
||||||
|
"button": "Takk nei"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/hvem.yaml": {
|
||||||
|
"question": "Hva er Gullberg?",
|
||||||
|
"description": "Et gammelt hus bak Ås kirke, eid av fire aksjonærer og drevet av styret, med Klingenberg Bygg som forvalter. Her er rommene som er ledige nå.",
|
||||||
|
"presents": {
|
||||||
|
"rom": {
|
||||||
|
"heading": "Rommene som er ledige",
|
||||||
|
"description": "Rommene som står ledige for dem som søker et sted å bo.",
|
||||||
|
"empty": "Ingen rom er ledige akkurat nå, men nye kommer når noen flytter ut."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"heading": "Det styret har vedtatt",
|
||||||
|
"description": "Det som gjelder for huset og driften, slik styret har satt det.",
|
||||||
|
"items": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/index.yaml": {
|
||||||
|
"question": "Trenger du et rimelig rom i Ås?",
|
||||||
|
"description": "Gullberg er et gammelt hus bak Ås kirke, et kort stykke fra NMBU. Eierne driver det selv, og Klingenberg Bygg forvalter huset og vedlikeholdet.",
|
||||||
|
"opener": {
|
||||||
|
"heading": "Et rimelig sted å bo, nær NMBU",
|
||||||
|
"description": "Rommene leies ut for et år av gangen, til en pris som går opp med studielånet. Studenter går foran, men alle kan søke.",
|
||||||
|
"features": {
|
||||||
|
"rom": {
|
||||||
|
"heading": "Rom til leie, studenter først",
|
||||||
|
"description": "Du leier et rom i huset for et år av gangen. Studenter ved NMBU får rommene først; er det ledig, får andre også.",
|
||||||
|
"icon": "house"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"who": {
|
||||||
|
"heading": "Hva er Gullberg?",
|
||||||
|
"description": "Fire aksjonærer eier huset sammen og driver det selv, med Klingenberg Bygg som forvalter.",
|
||||||
|
"button": "Se huset og rommene"
|
||||||
|
},
|
||||||
|
"shopping": {
|
||||||
|
"heading": "Hva som er gjort, og hva som mangler",
|
||||||
|
"description": "Det styret har vedtatt for huset: planene, med pris og tid der det er kjent.",
|
||||||
|
"items": {
|
||||||
|
"status-liste": {
|
||||||
|
"heading": "Siste fra lista",
|
||||||
|
"description": "Punktene styret har tatt stilling til, ferskest øverst.",
|
||||||
|
"link": "Se hele lista",
|
||||||
|
"empty": "Snart kommer de første punktene på lista.",
|
||||||
|
"icon": "clipboard-list"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"doors": {},
|
||||||
|
"needs": {
|
||||||
|
"leie-rom": {
|
||||||
|
"heading": "Søk om et rom",
|
||||||
|
"description": "Fortell litt om deg og hva du gjør til daglig. Studenter går foran, men alle kan søke.",
|
||||||
|
"button": "Søk rom",
|
||||||
|
"encouragement": "Det tar bare et par minutter, og du hører fra styret så snart et rom er ledig.",
|
||||||
|
"icon": "key",
|
||||||
|
"paused": "Søknader er satt på pause",
|
||||||
|
"requires_title": "Dette trenger vi",
|
||||||
|
"requires_description": "Navn, e-post og hvilket lærested du går på.",
|
||||||
|
"requires_icon": "clipboard-list",
|
||||||
|
"road_title": "Veien videre",
|
||||||
|
"road_description": "Søknaden havner hos styret, som inviterer deg på visning av et ledig rom. Sier du ja til tilbudet, signerer du leieavtalen og flytter inn.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem leser søknaden",
|
||||||
|
"who_description": "Styret i Gullberg leser søknaden din, og du når dem i mellomtiden på bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Søknadsskjema",
|
||||||
|
"form_description": "Fyll inn det du vet nå - resten kan vente til visningen.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"email": "E-post",
|
||||||
|
"telefon": "Telefon",
|
||||||
|
"innflytting": "Ønsket innflytting",
|
||||||
|
"om": "Litt om deg",
|
||||||
|
"situasjon": "Hva gjør du til daglig?"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"name": "Kari Nordmann",
|
||||||
|
"email": "kari@student.no",
|
||||||
|
"telefon": "123 45 678",
|
||||||
|
"innflytting": "August 2026",
|
||||||
|
"om": "Jeg studerer medisin og trenger et rolig sted å bo nær campus.",
|
||||||
|
"situasjon": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/klingenberg/index.yaml": {
|
||||||
|
"question": "Hva trenger huset nå?",
|
||||||
|
"description": "Her samler Klingenberg Bygg det leietakerne melder inn, og styret ser overslagene før de godkjennes.",
|
||||||
|
"confirm": "Bekreft",
|
||||||
|
"buckets": {
|
||||||
|
"feil": {
|
||||||
|
"heading": "Se hva som er meldt inn av feil",
|
||||||
|
"description": "Her ser du det leietakerne har meldt, fra ny melding til den er utbedret.",
|
||||||
|
"moves": {
|
||||||
|
"befaring->overslag": "Lag overslag",
|
||||||
|
"befaring->avvist": "Avvis saken",
|
||||||
|
"godkjent->utbedret": "Meld utbedret",
|
||||||
|
"open->befaring": "Sett opp befaring",
|
||||||
|
"open->avvist": "Avvis saken",
|
||||||
|
"overslag->avvist": "Avvis overslaget"
|
||||||
|
},
|
||||||
|
"list_title": "Meldte feil",
|
||||||
|
"list_note": "Hver rad er en feil som er meldt inn; knappene under flytter den videre til befaring, overslag eller utbedret."
|
||||||
|
},
|
||||||
|
"overslag": {
|
||||||
|
"heading": "Følg opp overslagene dine",
|
||||||
|
"description": "Her ser du overslagene du har levert, og om styret har godkjent dem.",
|
||||||
|
"moves": {
|
||||||
|
"godkjent->fakturert": "Merk fakturert"
|
||||||
|
},
|
||||||
|
"list_title": "Leverte overslag",
|
||||||
|
"list_note": "Hver rad er et overslag du har levert; marker det fakturert når regningen er sendt."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"overslag": {
|
||||||
|
"heading": "Se alle overslag i ett regnskap",
|
||||||
|
"description": "Her finner du overslagene samlet, uansett hvem som har meldt feilen.",
|
||||||
|
"button": "Åpne overslag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invites": {
|
||||||
|
"klingenberg": {
|
||||||
|
"heading": "Gi noen hos Klingenberg Bygg tilgang",
|
||||||
|
"description": "Personen får en konto og en lenke som gjelder én gang, så de kan logge inn og se feilene selv.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Inviter ny person",
|
||||||
|
"form_description": "Ha navn, telefon og e-post klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invited": {
|
||||||
|
"heading": "Hold oversikt over inviterte",
|
||||||
|
"description": "Her ser du hvem som er invitert, og om de har tatt lenken i bruk.",
|
||||||
|
"done": "Tatt i bruk",
|
||||||
|
"withdraw": "Trekk tilbake",
|
||||||
|
"again": "Send på nytt",
|
||||||
|
"list_title": "Sendte invitasjoner",
|
||||||
|
"list_note": "Hver rad er en invitasjon; send den på nytt hvis lenken har gått ut, eller trekk den tilbake."
|
||||||
|
},
|
||||||
|
"suggestions": null
|
||||||
|
},
|
||||||
|
"questions/klingenberg/overslag-sent.yaml": {
|
||||||
|
"question": "Hva skjer med overslaget du nettopp sendte inn?",
|
||||||
|
"description": "Det ligger nå hos styret, med beløp og hva det dekker. De tar stilling til det og fører det videre i regnskapet.",
|
||||||
|
"next_heading": "Styret vurderer overslaget",
|
||||||
|
"next_description": "Du hører fra dem hvis de trenger mer, ellers venter du på svar om det blir godkjent, avvist eller fakturert."
|
||||||
|
},
|
||||||
|
"questions/klingenberg/overslag.yaml": {
|
||||||
|
"heading": "Overslaget går til styret",
|
||||||
|
"description": "Når du har sett på en feil og satt en pris, sender du overslaget hit, så styret kan ta stilling til det.",
|
||||||
|
"button": "Send overslag",
|
||||||
|
"encouragement": "Styret ser det i løpet av kort tid, og det følger saken videre til regnskapet.",
|
||||||
|
"icon": "receipt",
|
||||||
|
"paused": "Ta ikke mot nye overslag nå",
|
||||||
|
"requires_title": "Krev tittel",
|
||||||
|
"requires_description": "Krev beskrivelse",
|
||||||
|
"requires_icon": "Krev ikon",
|
||||||
|
"road_title": "Veien videre",
|
||||||
|
"road_description": "Styret godkjenner eller avviser overslaget. Blir det godkjent og arbeidet utført, blir det fakturert.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem leser det",
|
||||||
|
"who_description": "Styret i Gullberg leser overslaget og tar stilling til det, du når dem på bl@klingenbergbygg.no i mellomtiden.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Overslag på feil",
|
||||||
|
"form_description": "Ha prisen og hva den dekker klart før du sender.",
|
||||||
|
"labels": {
|
||||||
|
"gjelder": "Hva overslaget gjelder",
|
||||||
|
"belop": "Beløp",
|
||||||
|
"hva-det-dekker": "Hva beløpet dekker",
|
||||||
|
"dokument": "Dokument"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"gjelder": "Lekkasje på badet i rom 4",
|
||||||
|
"belop": "12 500 kr",
|
||||||
|
"hva-det-dekker": "Ny membran, fliser og arbeidstid, to dagers arbeid",
|
||||||
|
"dokument": "overslag-rom4.pdf"
|
||||||
|
},
|
||||||
|
"question": "Hva vil det koste å rette feilen?",
|
||||||
|
"page_description": "Overslaget går til styret, som godkjenner eller avviser det før det føres i regnskapet."
|
||||||
|
},
|
||||||
|
"questions/leietakere/feil-sent.yaml": {
|
||||||
|
"question": "Hva skjer nå med feilen jeg meldte?",
|
||||||
|
"description": "Meldingen er sendt til Klingenberg Bygg, som drar på befaring og vurderer hva den koster å rette.",
|
||||||
|
"next_heading": "Klingenberg Bygg ser på saken",
|
||||||
|
"next_description": "De befarer feilen og lager et overslag som styret tar stilling til."
|
||||||
|
},
|
||||||
|
"questions/leietakere/feil.yaml": {
|
||||||
|
"heading": "Meld fra om noe som er galt i huset",
|
||||||
|
"description": "Fant du noe som ikke virker eller er ødelagt? Si fra, så ser styret på det.",
|
||||||
|
"button": "Send feilen",
|
||||||
|
"encouragement": "Tar et par minutter, og styret får beskjed med én gang.",
|
||||||
|
"icon": "wrench",
|
||||||
|
"paused": "Ta imot nye feilmeldinger",
|
||||||
|
"requires_title": "Overskrift",
|
||||||
|
"requires_description": "Forklaring",
|
||||||
|
"requires_icon": "Ikon",
|
||||||
|
"road_title": "Hva skjer videre",
|
||||||
|
"road_description": "Styret ser på feilen og avtaler en befaring. Deretter henter de inn et overslag, godkjenner det, og feilen rettes opp – eller avvises, med beskjed om hvorfor.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem leser dette",
|
||||||
|
"who_description": "Styret i Gullberg får meldingen og følger den opp; haster det, send en e-post til bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Meld en feil",
|
||||||
|
"form_description": "Fortell hva som er galt og hvor, gjerne med bilde.",
|
||||||
|
"labels": {
|
||||||
|
"hva": "Hva er galt",
|
||||||
|
"hvor": "Hvor i huset",
|
||||||
|
"beskrivelse": "Beskriv feilen",
|
||||||
|
"email": "E-post",
|
||||||
|
"bilde": "Bilde"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"hva": "Vannlekkasje under kjøkkenvasken",
|
||||||
|
"hvor": "Kjøkkenet i 2. etasje",
|
||||||
|
"beskrivelse": "Det drypper fra røret under vasken, har blitt verre siste uken",
|
||||||
|
"email": "navn@student.no",
|
||||||
|
"bilde": "Last opp et bilde av feilen"
|
||||||
|
},
|
||||||
|
"question": "Har du funnet noe i huset som bør rettes opp?",
|
||||||
|
"page_description": "Meldingen går rett til Klingenberg Bygg, som forvalter huset. De ser på det, og styret godkjenner det som koster penger."
|
||||||
|
},
|
||||||
|
"questions/leietakere/index.yaml": {
|
||||||
|
"question": "Hva kan vi gjøre for deg i huset?",
|
||||||
|
"description": "Feil leietakerne melder inn, og overslagene Klingenberg Bygg sender over, samlet på ett sted.",
|
||||||
|
"confirm": "Bekreft",
|
||||||
|
"buckets": {},
|
||||||
|
"links": {
|
||||||
|
"feil": {
|
||||||
|
"heading": "Har du funnet noe som er galt i huset?",
|
||||||
|
"description": "Meld fra, så går det rett til Klingenberg Bygg.",
|
||||||
|
"button": "Meld fra"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invites": {},
|
||||||
|
"invited": null,
|
||||||
|
"suggestions": null
|
||||||
|
},
|
||||||
|
"questions/received/leie-rom.yaml": {
|
||||||
|
"question": "Hva skjer med søknaden min nå?",
|
||||||
|
"description": "Styret går gjennom romsøknader fortløpende og svarer så snart det er en visning å tilby.",
|
||||||
|
"next_heading": "Du hører fra styret om en visning",
|
||||||
|
"next_description": "Styret tar kontakt på e-post eller telefon for å avtale tid. Får du tilbud om rommet, får du beskjed rett etter."
|
||||||
|
},
|
||||||
|
"questions/status/liste/index.yaml": {
|
||||||
|
"question": "Hva er vedtatt og gjort for huset?",
|
||||||
|
"description": "Her ligger det som styret har vedtatt og satt i verk, med pris og tid der det er kjent.",
|
||||||
|
"heading": "Det som er vedtatt og gjort",
|
||||||
|
"list_description": "Åpen liste over det styret har vedtatt for huset.",
|
||||||
|
"list_title": "Vedtatt og gjort",
|
||||||
|
"list_note": "Hver rad er én sak, med hva den gjelder, kostnad og tidspunkt der det er satt.",
|
||||||
|
"empty": "Ingen saker er publisert ennå.",
|
||||||
|
"answer": "Svar"
|
||||||
|
},
|
||||||
|
"questions/status/rom/index.yaml": {
|
||||||
|
"question": "Hvilke rom er ledige nå?",
|
||||||
|
"description": "Her finner du rommene som er ledige for innflytting, med bilde og en kort beskrivelse av hvert rom.",
|
||||||
|
"heading": "Ledige rom i huset",
|
||||||
|
"list_description": "Rommene som er ledige akkurat nå.",
|
||||||
|
"list_title": "Ledige rom",
|
||||||
|
"list_note": "Hver rad er et rom du kan søke på.",
|
||||||
|
"empty": "Ingen rom er ledige akkurat nå, men se gjerne innom igjen senere.",
|
||||||
|
"answer": "Svar"
|
||||||
|
},
|
||||||
|
"questions/styret/eierskifte-sent.yaml": {
|
||||||
|
"question": "Hva skjer nå med eierskiftesaken du sendte inn?",
|
||||||
|
"description": "Steget er registrert og legger seg på listen over eierskiftet. Aksjonærene ser det neste gang de ser på fremdriften.",
|
||||||
|
"next_heading": "Følg eierskiftet videre",
|
||||||
|
"next_description": "Du finner status og alle steg i oversikten over eierskiftet, og kan legge inn mer når noe nytt skjer."
|
||||||
|
},
|
||||||
|
"questions/styret/eierskifte.yaml": {
|
||||||
|
"heading": "Se hvor eierskiftet står",
|
||||||
|
"description": "Meld fra om neste steg i overgangen til selskapet, så ser aksjonærene og banken fremgangen.",
|
||||||
|
"button": "Legg inn steg",
|
||||||
|
"encouragement": "Tar et minutt, og listen oppdateres med én gang.",
|
||||||
|
"icon": "file-text",
|
||||||
|
"paused": "Satt på vent",
|
||||||
|
"requires_title": "Krever tittel",
|
||||||
|
"requires_description": "Krever beskrivelse",
|
||||||
|
"requires_icon": "Krever ikon",
|
||||||
|
"road_title": "Fra melding til ferdig",
|
||||||
|
"road_description": "Steget legges inn, så arbeides det med, og til slutt merkes det som ferdig.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem leser det",
|
||||||
|
"who_description": "Styret følger eierskiftet her; ring styret i mellomtiden hvis noe ikke kan vente.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Nytt steg i eierskiftet",
|
||||||
|
"form_description": "Skriv hva som er gjort, og legg ved dokumentet hvis det finnes et.",
|
||||||
|
"labels": {
|
||||||
|
"steg": "Hva er gjort",
|
||||||
|
"merknad": "Merknad",
|
||||||
|
"dokument": "Dokument"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"steg": "Skjøtet er sendt til tinglysing",
|
||||||
|
"merknad": "Megler venter på signatur fra den siste selgeren",
|
||||||
|
"dokument": "skjote-gullberg.pdf"
|
||||||
|
},
|
||||||
|
"question": "Hvor langt har eierskiftet kommet?",
|
||||||
|
"page_description": "Her legger styret inn stegene i overgangen til selskapet, så aksjonærene kan følge med. Banken får det den skal ha fra styret."
|
||||||
|
},
|
||||||
|
"questions/styret/husleie-sent.yaml": {
|
||||||
|
"question": "Hva skjer nå med husleia jeg registrerte?",
|
||||||
|
"description": "Betalingen er lagt inn og synlig for resten av styret, sammen med de andre rommenes status denne måneden.",
|
||||||
|
"next_heading": "Følg med i husleielista",
|
||||||
|
"next_description": "Der ser dere hvilke rom som har betalt og hva som er purret på."
|
||||||
|
},
|
||||||
|
"questions/styret/husleie.yaml": {
|
||||||
|
"heading": "Husleia for måneden, samlet ett sted",
|
||||||
|
"description": "Legg inn hva som er betalt per rom, så aksjonærene ser det og banken får sitt.",
|
||||||
|
"button": "Registrer husleie",
|
||||||
|
"encouragement": "Det tar et minutt per rom.",
|
||||||
|
"icon": "banknote",
|
||||||
|
"paused": "Tas ikke imot nå",
|
||||||
|
"requires_title": "Krever tittel",
|
||||||
|
"requires_description": "Krever beskrivelse",
|
||||||
|
"requires_icon": "Krever ikon",
|
||||||
|
"road_title": "Hva som skjer videre",
|
||||||
|
"road_description": "Du registrerer at husleia er betalt. Hvis et rom ikke har betalt, markeres det som purret til det kommer inn.",
|
||||||
|
"road_icon": "refresh-cw",
|
||||||
|
"who_title": "Hvem som ser dette",
|
||||||
|
"who_description": "Styret ser oversikten; har du spørsmål underveis, ta kontakt med styret på bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Registrer husleie",
|
||||||
|
"form_description": "Ha rom, måned og beløp klart.",
|
||||||
|
"labels": {
|
||||||
|
"rom": "Hvilket rom",
|
||||||
|
"maaned": "Hvilken måned",
|
||||||
|
"belop": "Beløp",
|
||||||
|
"betalt-dato": "Betalt dato"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"rom": "Rom 2",
|
||||||
|
"maaned": "Oktober 2026",
|
||||||
|
"belop": "4500",
|
||||||
|
"betalt-dato": "2026-10-05"
|
||||||
|
},
|
||||||
|
"question": "Hvilke rom har betalt husleie denne måneden?",
|
||||||
|
"page_description": "Her ser styret hvilke rom som har betalt husleia og hva som har gått til banken."
|
||||||
|
},
|
||||||
|
"questions/styret/index.yaml": {
|
||||||
|
"question": "Hva trenger styret å gjøre nå?",
|
||||||
|
"description": "Her samler styret alt som skjer i huset: søknader, feil, økonomi og eierskiftet, med knappene som flytter hver sak videre.",
|
||||||
|
"confirm": "Bekreft",
|
||||||
|
"buckets": {
|
||||||
|
"eierskifte": {
|
||||||
|
"heading": "Se hvor langt eierskiftet har kommet",
|
||||||
|
"description": "Følg stegene når huset går over til selskapet, med dokumentene på plass for aksjonærer og bank.",
|
||||||
|
"moves": {
|
||||||
|
"arbeid->ferdig": "Merk steget ferdig",
|
||||||
|
"open->arbeid": "Start arbeidet"
|
||||||
|
},
|
||||||
|
"list_title": "Steg i eierskiftet",
|
||||||
|
"list_note": "Hver rad er ett steg i overgangen, med merknad og dokument; knappen under flytter steget videre."
|
||||||
|
},
|
||||||
|
"feil": {
|
||||||
|
"heading": "Følg opp feil som er meldt inn",
|
||||||
|
"description": "Se hva leietakerne har funnet galt i huset, og send det videre til Klingenberg Bygg for befaring og overslag.",
|
||||||
|
"moves": {
|
||||||
|
"befaring->avvist": "Avvis feilen",
|
||||||
|
"open->avvist": "Avvis feilen",
|
||||||
|
"overslag->godkjent": "Godkjenn overslaget",
|
||||||
|
"overslag->avvist": "Avvis overslaget"
|
||||||
|
},
|
||||||
|
"list_title": "Meldte feil",
|
||||||
|
"list_note": "Hver rad er en feil meldt av en leietaker, med bilde og beskrivelse; knappene under sender den videre eller avviser den."
|
||||||
|
},
|
||||||
|
"husleie": {
|
||||||
|
"heading": "Hold oversikt over husleia hver måned",
|
||||||
|
"description": "Se hvilke rom som har betalt, og purr dem som ikke har gjort det.",
|
||||||
|
"moves": {
|
||||||
|
"open->betalt": "Merk som betalt",
|
||||||
|
"open->purret": "Send purring",
|
||||||
|
"purret->betalt": "Merk som betalt"
|
||||||
|
},
|
||||||
|
"list_title": "Husleie per rom",
|
||||||
|
"list_note": "Hver rad er husleia for ett rom én måned, med beløp og betalingsdato; knappene under merker betalt eller sender purring."
|
||||||
|
},
|
||||||
|
"liste": {
|
||||||
|
"heading": "Styr lista over det som er bra, mangler og er planlagt",
|
||||||
|
"description": "Vedta punktene, sett pris og tid, og publiser dem som leietakerne skal se.",
|
||||||
|
"moves": {
|
||||||
|
"open->vedtatt": "Vedta punktet",
|
||||||
|
"open->forkastet": "Forkast punktet",
|
||||||
|
"vedtatt->publisert": "Publiser punktet",
|
||||||
|
"vedtatt->forkastet": "Forkast punktet"
|
||||||
|
},
|
||||||
|
"list_title": "Punkter på lista",
|
||||||
|
"list_note": "Hver rad er ett punkt med slag, beskrivelse, kostnad og tid; knappene under vedtar, publiserer eller forkaster det."
|
||||||
|
},
|
||||||
|
"nedbetalingsplaner": {
|
||||||
|
"heading": "Følg nedbetalingsplanen fra aksjonærene til banken",
|
||||||
|
"description": "Følg planen fra forslag til bankens svar, med pengestrømmen alle fire aksjonærene står bak.",
|
||||||
|
"moves": {
|
||||||
|
"godkjent->sendt": "Send til banken",
|
||||||
|
"sendt->endringer": "Be om endringer",
|
||||||
|
"sendt->innvilget": "Merk som innvilget"
|
||||||
|
},
|
||||||
|
"list_title": "Nedbetalingsplaner",
|
||||||
|
"list_note": "Hver rad er en plan med lånebeløp, avdrag og pengestrøm; knappene under sender den til banken eller merker svaret."
|
||||||
|
},
|
||||||
|
"overslag": {
|
||||||
|
"heading": "Ta stilling til overslag fra Klingenberg Bygg",
|
||||||
|
"description": "Se beløpet for hver feil som er sett på, og godkjenn det som skal inn i regnskapet.",
|
||||||
|
"moves": {
|
||||||
|
"open->godkjent": "Godkjenn overslaget",
|
||||||
|
"open->avvist": "Avvis overslaget"
|
||||||
|
},
|
||||||
|
"list_title": "Overslag",
|
||||||
|
"list_note": "Hver rad er et overslag med beløp og hva det dekker; knappene under godkjenner eller avviser det."
|
||||||
|
},
|
||||||
|
"rapporter": {
|
||||||
|
"heading": "Send kvartalsrapporten til aksjonærene og banken",
|
||||||
|
"description": "Samle tallene for kvartalet, og send rapporten videre når den er klar.",
|
||||||
|
"moves": {
|
||||||
|
"open->sendt": "Send rapporten"
|
||||||
|
},
|
||||||
|
"list_title": "Kvartalsrapporter",
|
||||||
|
"list_note": "Hver rad er rapporten for ett kvartal, med leieinntekter, kostnader og saldo; knappen under sender den."
|
||||||
|
},
|
||||||
|
"rom": {
|
||||||
|
"heading": "Vis ledige rom frem til studentene",
|
||||||
|
"description": "Legg ut rommet som er blitt ledig, og merk det utleid når noen har flyttet inn.",
|
||||||
|
"moves": {
|
||||||
|
"open->utleid": "Merk som utleid"
|
||||||
|
},
|
||||||
|
"list_title": "Rom",
|
||||||
|
"list_note": "Hver rad er et rom med bilde og beskrivelse; knappen under merker det utleid."
|
||||||
|
},
|
||||||
|
"romsoknader": {
|
||||||
|
"heading": "Følg opp søknader om rom",
|
||||||
|
"description": "Se hvem som har søkt og hva de gjør til daglig (studenter går foran), sett opp visning, og før søknaden frem til signert leieavtale.",
|
||||||
|
"moves": {
|
||||||
|
"akseptert->signert": "Merk som signert",
|
||||||
|
"open->visning": "Sett opp visning",
|
||||||
|
"open->avslatt": "Avslå søknaden",
|
||||||
|
"tilbud->avslatt": "Avslå søknaden",
|
||||||
|
"visning->tilbud": "Gi tilbud om rom",
|
||||||
|
"visning->avslatt": "Avslå søknaden"
|
||||||
|
},
|
||||||
|
"list_title": "Søknader om rom",
|
||||||
|
"list_note": "Hver rad er en søknad, med hva søkeren gjør til daglig og ønsket innflytting. Studenter går foran. Knappene fører søknaden videre eller avslår den."
|
||||||
|
},
|
||||||
|
"saker": {
|
||||||
|
"heading": "Saker aksjonærene har meldt",
|
||||||
|
"description": "Ta saker aksjonærene melder inn, sett dem på sakslisten, og vedta eller avvis dem.",
|
||||||
|
"moves": {
|
||||||
|
"open->sakslisten": "Sett på sakslisten",
|
||||||
|
"open->avvist": "Avvis saken",
|
||||||
|
"sakslisten->vedtatt": "Vedta saken",
|
||||||
|
"sakslisten->avvist": "Avvis saken"
|
||||||
|
},
|
||||||
|
"list_title": "Saker",
|
||||||
|
"list_note": "Hver rad er en sak meldt inn av en aksjonær, med tittel og beskrivelse; knappene under setter den på sakslisten, vedtar eller avviser den."
|
||||||
|
},
|
||||||
|
"uker": {
|
||||||
|
"heading": "Bekreft ukene aksjonærene vil bruke huset",
|
||||||
|
"description": "Se hvem som har meldt seg på en uke, og bekreft eller avslå den.",
|
||||||
|
"moves": {
|
||||||
|
"open->bekreftet": "Bekreft uken",
|
||||||
|
"open->avslatt": "Avslå uken"
|
||||||
|
},
|
||||||
|
"list_title": "Ønskede uker",
|
||||||
|
"list_note": "Hver rad er en uke en aksjonær har meldt seg på, med datoer og merknad; knappene under bekrefter eller avslår den."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"ledig-rom": {
|
||||||
|
"heading": "Legg ut et rom som er blitt ledig",
|
||||||
|
"description": "Skriv tittel, legg ved bilde og beskriv rommet, så studentene kan søke.",
|
||||||
|
"button": "Legg ut rom"
|
||||||
|
},
|
||||||
|
"liste": {
|
||||||
|
"heading": "Legg til et nytt punkt på lista",
|
||||||
|
"description": "Skriv hva som er bra, mangler eller planlagt, med pris og tid om du har det.",
|
||||||
|
"button": "Legg til punkt"
|
||||||
|
},
|
||||||
|
"rapport": {
|
||||||
|
"heading": "Lag kvartalets rapport",
|
||||||
|
"description": "Fyll inn tallene for kvartalet, så aksjonærene og banken får det de skal ha.",
|
||||||
|
"button": "Lag rapport"
|
||||||
|
},
|
||||||
|
"nedbetalingsplan": {
|
||||||
|
"heading": "Lag en ny nedbetalingsplan",
|
||||||
|
"description": "Skriv lånebeløp, avdrag og pengestrøm, så banken har noe å ta stilling til.",
|
||||||
|
"button": "Lag plan"
|
||||||
|
},
|
||||||
|
"husleie": {
|
||||||
|
"heading": "Registrer husleie for et rom",
|
||||||
|
"description": "Før inn beløp og måned når husleia for et rom er kommet inn.",
|
||||||
|
"button": "Registrer husleie"
|
||||||
|
},
|
||||||
|
"eierskifte": {
|
||||||
|
"heading": "Legg til et steg i eierskiftet",
|
||||||
|
"description": "Beskriv steget og legg ved dokument, så aksjonærene ser fremgangen.",
|
||||||
|
"button": "Legg til steg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invites": {
|
||||||
|
"aksjonaerer": {
|
||||||
|
"heading": "Gi en aksjonær tilgang til sin side",
|
||||||
|
"description": "Send en invitasjon så aksjonæren får konto og engangslenke.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Inviter aksjonær",
|
||||||
|
"form_description": "Ha navn, e-post og telefon klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"klingenberg": {
|
||||||
|
"heading": "Gi Klingenberg Bygg tilgang til feilmeldinger",
|
||||||
|
"description": "Send en invitasjon så de får konto og engangslenke.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Inviter Klingenberg Bygg",
|
||||||
|
"form_description": "Ha navn, e-post og telefon klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"leietakere": {
|
||||||
|
"heading": "Gi en leietaker tilgang til sin side",
|
||||||
|
"description": "Send en invitasjon så leietakeren får konto og engangslenke.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Inviter leietaker",
|
||||||
|
"form_description": "Ha navn, e-post og telefon klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"styret": {
|
||||||
|
"heading": "Gi et styremedlem tilgang til desken",
|
||||||
|
"description": "Send en invitasjon så styremedlemmet får konto og engangslenke.",
|
||||||
|
"button": "Send invitasjon",
|
||||||
|
"form_title": "Inviter styremedlem",
|
||||||
|
"form_description": "Ha navn, e-post og telefon klart.",
|
||||||
|
"labels": {
|
||||||
|
"name": "Navn",
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invited": {
|
||||||
|
"heading": "Se hvem som er invitert og hvor langt de har kommet",
|
||||||
|
"description": "Følg med på invitasjoner som er sendt, og send en ny lenke om den gamle har løpt ut.",
|
||||||
|
"done": "Tatt i bruk",
|
||||||
|
"withdraw": "Trekk tilbake",
|
||||||
|
"again": "Send på nytt",
|
||||||
|
"list_title": "Sendte invitasjoner",
|
||||||
|
"list_note": "Hver rad er en invitasjon sendt til en person; knappene under trekker den tilbake eller sender den på nytt."
|
||||||
|
},
|
||||||
|
"suggestions": {
|
||||||
|
"heading": "Ta stilling til foreslåtte endringer",
|
||||||
|
"description": "Se hva andre har foreslått å endre på sidene de har ansvar for.",
|
||||||
|
"list_title": "Foreslåtte endringer",
|
||||||
|
"list_note": "Hver rad er en foreslått endring; knappene under tar den til vurdering, godtar eller avslår den.",
|
||||||
|
"looking": "Tar en titt",
|
||||||
|
"changed": "Endret",
|
||||||
|
"declined": "Avslått"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/styret/ledig-rom-sent.yaml": {
|
||||||
|
"question": "Hva skjer med rommet du nettopp la ut?",
|
||||||
|
"description": "Rommet er lagt inn og synlig for dem som søker. Dere ser det under ledige rom til det er leid ut.",
|
||||||
|
"next_heading": "Studenter kan søke på rommet nå",
|
||||||
|
"next_description": "Søknader kommer inn etter hvert, og dere i styret ser dem sammen med de andre søknadene.",
|
||||||
|
"see_all": {
|
||||||
|
"heading": "Se alle rommene og status",
|
||||||
|
"description": "Full oversikt over hvilke rom som er ledige og hvilke som er leid ut.",
|
||||||
|
"button": "Se rommene"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/styret/ledig-rom.yaml": {
|
||||||
|
"heading": "Meld inn et rom som er ledig",
|
||||||
|
"description": "Når et rom står tomt, legger du det ut her, så ser de som søker det først.",
|
||||||
|
"button": "Legg ut rommet",
|
||||||
|
"encouragement": "Så snart det er lagt ut, kan folk søke på det.",
|
||||||
|
"icon": "key",
|
||||||
|
"paused": "Ta rommet ned fra listen for nå",
|
||||||
|
"requires_title": "Krever tittel",
|
||||||
|
"requires_description": "Krever beskrivelse",
|
||||||
|
"requires_icon": "Krever bilde",
|
||||||
|
"road_title": "Fra ledig til utleid",
|
||||||
|
"road_description": "Rommet ligger ute til det er utleid.",
|
||||||
|
"road_icon": "key",
|
||||||
|
"who_title": "Hvem ser dette",
|
||||||
|
"who_description": "Studenter som søker rom ser dette, og styret kan nås på bl@klingenbergbygg.no i mellomtiden.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Om rommet",
|
||||||
|
"form_description": "Et bilde og en kort beskrivelse er nok til at de som søker kan se seg om.",
|
||||||
|
"labels": {
|
||||||
|
"tittel": "Tittel på rommet",
|
||||||
|
"bilde": "Bilde av rommet",
|
||||||
|
"om": "Om rommet"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"tittel": "Rom i 2. etasje, mot gata",
|
||||||
|
"bilde": "",
|
||||||
|
"om": "Lyst rom med eget bad, nær kjøkken og felles stue."
|
||||||
|
},
|
||||||
|
"question": "Hvilket rom er ledig nå?",
|
||||||
|
"page_description": "Legg ut et rom som har blitt ledig, så de som søker kan se det."
|
||||||
|
},
|
||||||
|
"questions/styret/liste-sent.yaml": {
|
||||||
|
"question": "Hva skjer med punktet nå?",
|
||||||
|
"description": "Forslaget ditt er lagret. Styret tar det opp til behandling.",
|
||||||
|
"next_heading": "Styret vedtar eller forkaster punktet",
|
||||||
|
"next_description": "Det blir stående til styret har tatt stilling, med pris og tid synlig for alle.",
|
||||||
|
"see_all": {
|
||||||
|
"heading": "Se hele lista",
|
||||||
|
"description": "Alt som er meldt inn, vedtatt eller publisert ligger samlet.",
|
||||||
|
"button": "Til lista"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"questions/styret/liste.yaml": {
|
||||||
|
"heading": "Listen over huset og planene",
|
||||||
|
"description": "Legg til det som er bra, det som mangler og det som er planlagt, med pris og tid der dere vet det.",
|
||||||
|
"button": "Legg til punkt",
|
||||||
|
"encouragement": "Det havner på listen med en gang, og dere kan vedta det senere.",
|
||||||
|
"icon": "list",
|
||||||
|
"paused": "Listen er stengt for nye punkter",
|
||||||
|
"requires_title": "Krev tittel",
|
||||||
|
"requires_description": "Krev beskrivelse",
|
||||||
|
"requires_icon": "Krev ikon",
|
||||||
|
"road_title": "Fra forslag til vedtak",
|
||||||
|
"road_description": "Punktet legges inn, styret tar stilling til det og vedtar eller forkaster, og det som vedtas blir publisert på listen alle ser.",
|
||||||
|
"road_icon": "check-circle",
|
||||||
|
"who_title": "Hvem ser listen",
|
||||||
|
"who_description": "Styret fører og vedtar listen; ta kontakt på bl@klingenbergbygg.no om noe haster.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Nytt punkt på listen",
|
||||||
|
"form_description": "Skriv kort hva det gjelder, og ta med pris og tid hvis dere har tall.",
|
||||||
|
"labels": {
|
||||||
|
"tittel": "Tittel",
|
||||||
|
"slag": "Hva slags punkt",
|
||||||
|
"beskrivelse": "Beskrivelse",
|
||||||
|
"kostnad": "Kostnad",
|
||||||
|
"naar": "Når"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"tittel": "Nytt kjøkken i andre etasje",
|
||||||
|
"slag": "Mangler",
|
||||||
|
"beskrivelse": "Kjøkkenet er slitt og trenger nye hvitevarer og benkeplate.",
|
||||||
|
"kostnad": "ca. 45 000 kr",
|
||||||
|
"naar": "Våren 2027"
|
||||||
|
},
|
||||||
|
"question": "Hva bør med på listen over huset og driften?",
|
||||||
|
"page_description": "Punktene går inn på listen styret fører over det som er bra, det som mangler og det som er planlagt."
|
||||||
|
},
|
||||||
|
"questions/styret/nedbetalingsplan-sent.yaml": {
|
||||||
|
"question": "Hvor er nedbetalingsplanen nå?",
|
||||||
|
"description": "Den ligger nå hos aksjonærene. Når alle fire har godkjent den, sender styret den til banken og fører inn svaret.",
|
||||||
|
"next_heading": "Følg svaret fra banken",
|
||||||
|
"next_description": "Når banken svarer, oppdaterer dere planen med godkjent, innvilget eller endringer – så de andre aksjonærene ser hvor den står."
|
||||||
|
},
|
||||||
|
"questions/styret/nedbetalingsplan.yaml": {
|
||||||
|
"heading": "Nedbetalingsplanen alle fire står bak",
|
||||||
|
"description": "Legg fram planen for lånet, så banken og aksjonærene vet hvordan det skal betales ned.",
|
||||||
|
"button": "Send plan",
|
||||||
|
"encouragement": "Styret tar den videre til banken så snart alle fire har sagt ja.",
|
||||||
|
"icon": "banknote",
|
||||||
|
"paused": "Satt på pause",
|
||||||
|
"requires_title": "Krever tittel",
|
||||||
|
"requires_description": "Krever beskrivelse",
|
||||||
|
"requires_icon": "Krever ikon",
|
||||||
|
"road_title": "Veien videre",
|
||||||
|
"road_description": "Planen går til godkjenning hos aksjonærene, så sendes den til banken. Sier banken ja, er den innvilget; ber den om endringer, kommer det en ny runde.",
|
||||||
|
"road_icon": "route",
|
||||||
|
"who_title": "Hvem ser den",
|
||||||
|
"who_description": "Styret og aksjonærene ser planen; spørsmål før den er sendt går til styret på bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Nedbetalingsplan",
|
||||||
|
"form_description": "Ha lånebeløp, avdrag og når det første trekket kommer, klart.",
|
||||||
|
"labels": {
|
||||||
|
"tittel": "Tittel",
|
||||||
|
"laanebelop": "Lånebeløp",
|
||||||
|
"maanedlig-avdrag": "Månedlig avdrag",
|
||||||
|
"forste-trekk": "Første trekk",
|
||||||
|
"pengestrom": "Hvordan pengene flyter",
|
||||||
|
"dokument": "Dokument"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"tittel": "Nedbetalingsplan 2026",
|
||||||
|
"laanebelop": "2 400 000 kr",
|
||||||
|
"maanedlig-avdrag": "18 000 kr",
|
||||||
|
"forste-trekk": "1. desember 2026",
|
||||||
|
"pengestrom": "Husleie dekker avdraget, timeshare går til vedlikehold, overskudd settes av til buffer",
|
||||||
|
"dokument": "Last opp plan eller avtaleutkast"
|
||||||
|
},
|
||||||
|
"question": "Hvordan skal lånet betales ned, og står alle fire bak det?",
|
||||||
|
"page_description": "Planen går til aksjonærene og banken, og viser hvordan lånet skal betales ned."
|
||||||
|
},
|
||||||
|
"questions/styret/rapport-sent.yaml": {
|
||||||
|
"question": "Hvor går rapporten nå?",
|
||||||
|
"description": "Den er sendt. Aksjonærene får tallene, og det som skal til banken går dit.",
|
||||||
|
"next_heading": "Neste rapport venter til kvartalet er omme",
|
||||||
|
"next_description": "Neste rapport legger styret inn når kvartalet er omme."
|
||||||
|
},
|
||||||
|
"questions/styret/rapport.yaml": {
|
||||||
|
"heading": "Rapporten som viser hvordan det går",
|
||||||
|
"description": "Hvert kvartal legger styret inn tallene, så aksjonærene ser status og banken får det den skal ha.",
|
||||||
|
"button": "Send rapport",
|
||||||
|
"encouragement": "Så snart den er sendt, ligger den klar for aksjonærene og banken.",
|
||||||
|
"icon": "file-text",
|
||||||
|
"paused": "Sett rapportering på pause",
|
||||||
|
"requires_title": "Krev tittel",
|
||||||
|
"requires_description": "Krev beskrivelse",
|
||||||
|
"requires_icon": "Krev ikon",
|
||||||
|
"road_title": "Hva skjer videre",
|
||||||
|
"road_description": "Rapporten sendes til aksjonærene og banken. Når den er ute, merkes den som sendt.",
|
||||||
|
"road_icon": "send",
|
||||||
|
"who_title": "Hvem leser den",
|
||||||
|
"who_description": "Styret står for rapportene; har du spørsmål underveis, ta kontakt på bl@klingenbergbygg.no.",
|
||||||
|
"who_icon": "users",
|
||||||
|
"form_title": "Kvartalets tall",
|
||||||
|
"form_description": "Ha regnskapstallene for kvartalet klare før du fyller ut.",
|
||||||
|
"labels": {
|
||||||
|
"periode": "Hvilket kvartal gjelder dette",
|
||||||
|
"leieinntekter": "Leieinntekter",
|
||||||
|
"timeshare": "Inntekter fra ukene",
|
||||||
|
"driftskostnader": "Driftskostnader",
|
||||||
|
"renter-og-avdrag": "Renter og avdrag til banken",
|
||||||
|
"saldo": "Saldo ved kvartalets slutt",
|
||||||
|
"kommentar": "Kommentar til tallene",
|
||||||
|
"dokument": "Regnskapsdokument"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"periode": "3. kvartal 2026",
|
||||||
|
"leieinntekter": "54 000",
|
||||||
|
"timeshare": "12 000",
|
||||||
|
"driftskostnader": "18 500",
|
||||||
|
"renter-og-avdrag": "22 000",
|
||||||
|
"saldo": "145 000",
|
||||||
|
"kommentar": "Ett rom stod tomt i juli, ellers som normalt",
|
||||||
|
"dokument": "kvartalsregnskap-q3-2026.pdf"
|
||||||
|
},
|
||||||
|
"question": "Hvordan gikk kvartalet, i tall?",
|
||||||
|
"page_description": "Rapporten går til aksjonærene og banken, så de ser inntekter, kostnader og saldo for kvartalet."
|
||||||
|
},
|
||||||
|
"site.yaml": {
|
||||||
|
"invite": {
|
||||||
|
"subject": "Du er invitert til Gullberg",
|
||||||
|
"body": "Hei {name},\n\nDu er invitert inn på Gullberg sin side, som {group}.\n\nVelg passord her før {expires}:\n{link}\n\nBrukernavnet ditt er {username}. Etterpå logger du inn på {site}.\n\nHilsen styret i Gullberg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
-8
@@ -973,16 +973,37 @@ fn Hero(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let has_slides = slideshow.is_some();
|
let has_slides = slideshow.is_some();
|
||||||
let mark = move || view! {
|
// In the page's ink (`wordmark_ink`): a span the file masks, sized
|
||||||
<a class="wordmark" class:wordmark-house=house_wordmark href="/">
|
// by the file's own proportions through an invisible copy of it,
|
||||||
<img src=wordmark.clone() alt=site_title.clone() style=wordmark_style.clone()/>
|
// and named for screen readers as the image would have been.
|
||||||
</a>
|
let ink = site.wordmark_ink;
|
||||||
|
let mark = move || {
|
||||||
|
if ink {
|
||||||
|
let mask = format!("--wordmark-src:url(\"{}\")", wordmark.replace('"', "%22"));
|
||||||
|
view! {
|
||||||
|
<a class="wordmark wordmark-ink" href="/" aria-label=site_title.clone()>
|
||||||
|
<span class="wordmark-ink-mark" style=mask>
|
||||||
|
<img src=wordmark.clone() alt="" aria-hidden="true" style=wordmark_style.clone()/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
.into_any()
|
||||||
|
} else {
|
||||||
|
view! {
|
||||||
|
<a class="wordmark" class:wordmark-house=house_wordmark href="/">
|
||||||
|
<img src=wordmark.clone() alt=site_title.clone() style=wordmark_style.clone()/>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
.into_any()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let top = view! { <div class="hero-top">{mark()}{session}</div> };
|
||||||
|
let (bar_top, copy_top) = if has_slides { (Some(top), None) } else { (None, Some(top)) };
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<header class="hero" class:hero-module=has_module class:hero-slides=has_slides>
|
<header class="hero" class:hero-module=has_module class:hero-slides=has_slides>
|
||||||
<Announcements current_id=current_id/>
|
<Announcements current_id=current_id/>
|
||||||
{session}
|
|
||||||
{has_module
|
{has_module
|
||||||
.then(|| {
|
.then(|| {
|
||||||
view! {
|
view! {
|
||||||
@@ -993,10 +1014,12 @@ fn Hero(
|
|||||||
// With pictures, the wordmark is a bar of its own above
|
// With pictures, the wordmark is a bar of its own above
|
||||||
// them and the question comes under; without, the wordmark
|
// them and the question comes under; without, the wordmark
|
||||||
// heads the copy as it always has.
|
// heads the copy as it always has.
|
||||||
{has_slides.then(|| view! { <div class="hero-bar">{mark()}</div> })}
|
// Who is here sits on the wordmark's own line, not on a row
|
||||||
|
// of its own above it.
|
||||||
|
{bar_top.map(|top| view! { <div class="hero-bar">{top}</div> })}
|
||||||
{slideshow.map(|show| view! { <SlideStrip show=show/> })}
|
{slideshow.map(|show| view! { <SlideStrip show=show/> })}
|
||||||
<div class="hero-copy">
|
<div class="hero-copy">
|
||||||
{(!has_slides).then(|| mark())}
|
{copy_top}
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<p inner_html=render_inline_markdown(&description)></p>
|
<p inner_html=render_inline_markdown(&description)></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -3059,10 +3082,16 @@ pub async fn submit_answer(
|
|||||||
let Some((name, who)) = crate::people::who_for(&responses) else {
|
let Some((name, who)) = crate::people::who_for(&responses) else {
|
||||||
return Err(ServerFnError::new("an invite needs a name, and a phone number or an email"));
|
return Err(ServerFnError::new("an invite needs a name, and a phone number or an email"));
|
||||||
};
|
};
|
||||||
let invited = people
|
let mut invited = people
|
||||||
.invite(&state, &name, &who, &invite.group, &principal_name(&principal))
|
.invite(&state, &name, &who, &invite.group, &principal_name(&principal))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ServerFnError::new(format!("invite failed: {e}")))?;
|
.map_err(|e| ServerFnError::new(format!("invite failed: {e}")))?;
|
||||||
|
// This invite's own words, and the form they draw on: kept with
|
||||||
|
// the invite, so sending it again says the same thing.
|
||||||
|
invited.mail = invite.mail.clone();
|
||||||
|
if invited.mail.is_some() {
|
||||||
|
invited.fields = responses.as_object().cloned().unwrap_or_default();
|
||||||
|
}
|
||||||
if let Some(map) = responses.as_object_mut() {
|
if let Some(map) = responses.as_object_mut() {
|
||||||
map.insert("invited".to_string(), serde_json::to_value(&invited).unwrap_or_default());
|
map.insert("invited".to_string(), serde_json::to_value(&invited).unwrap_or_default());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,6 +391,13 @@ pub struct Alternative {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Invite {
|
pub struct Invite {
|
||||||
pub group: String,
|
pub group: String,
|
||||||
|
/// This invite's own mail, in place of the site's (`mail.invite`):
|
||||||
|
/// an invitation to a conversation at an hour, say. Besides
|
||||||
|
/// `{name}`, `{link}`, `{expires}`, `{username}`, `{group}`,
|
||||||
|
/// `{site}` and `{site_name}`, every field of the form is a
|
||||||
|
/// placeholder; a date and time is written out in words.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mail: Option<MailTemplate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
@@ -767,6 +774,14 @@ pub struct SiteConfig {
|
|||||||
/// intrinsic pixel height.
|
/// intrinsic pixel height.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub wordmark_height: Option<f32>,
|
pub wordmark_height: Option<f32>,
|
||||||
|
/// Draw an SVG wordmark in the page's own ink instead of its own
|
||||||
|
/// colours: the file becomes a mask over the text colour, so it
|
||||||
|
/// follows the page's light and dark (and any theme a site adds),
|
||||||
|
/// where an `<img>` only ever knows the system's. Opacity in the
|
||||||
|
/// file still shows, so a lighter line stays lighter. For a
|
||||||
|
/// one-colour mark; a mark with a colour of its own keeps `false`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub wordmark_ink: bool,
|
||||||
/// BCP 47-ish language code for the portal's own chrome strings
|
/// BCP 47-ish language code for the portal's own chrome strings
|
||||||
/// ("Asked by", the nav lead...) and the html lang attribute.
|
/// ("Asked by", the nav lead...) and the html lang attribute.
|
||||||
/// `None` means "en"; unknown codes fall back to English per key.
|
/// `None` means "en"; unknown codes fall back to English per key.
|
||||||
|
|||||||
+35
-1
@@ -416,6 +416,14 @@ pub async fn on_responsible(state: &crate::server::AppState, invited: &crate::pe
|
|||||||
.map(|t| t.format("%Y-%m-%d %H:%M UTC").to_string())
|
.map(|t| t.format("%Y-%m-%d %H:%M UTC").to_string())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut vars = serde_json::Map::new();
|
let mut vars = serde_json::Map::new();
|
||||||
|
// The form's own fields first, so the invite's fixed placeholders
|
||||||
|
// win a clash; a date and time reads as one.
|
||||||
|
for (key, value) in &invited.fields {
|
||||||
|
let text = value.as_str().map(|v| spoken_datetime(v, norwegian).unwrap_or_else(|| v.to_string()));
|
||||||
|
if let Some(text) = text {
|
||||||
|
vars.insert(key.clone(), text.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
vars.insert("name".into(), invited.display_name.clone().into());
|
vars.insert("name".into(), invited.display_name.clone().into());
|
||||||
vars.insert("username".into(), invited.username.clone().into());
|
vars.insert("username".into(), invited.username.clone().into());
|
||||||
vars.insert("questions".into(), list.into());
|
vars.insert("questions".into(), list.into());
|
||||||
@@ -553,6 +561,25 @@ pub async fn on_edit_suggestion(state: &crate::server::AppState, question_id: &s
|
|||||||
/// Built in, since portal knows everything about it; `mail.invite` in
|
/// Built in, since portal knows everything about it; `mail.invite` in
|
||||||
/// site.yaml rewords it. `{name}`, `{email}`, `{group}`, `{link}`,
|
/// site.yaml rewords it. `{name}`, `{email}`, `{group}`, `{link}`,
|
||||||
/// `{site}`, `{expires}`.
|
/// `{site}`, `{expires}`.
|
||||||
|
/// A form's date and time (`2026-10-06T15:00`, from a `datetime-local`
|
||||||
|
/// field) as a person says it: "tirsdag 6. oktober kl. 15:00", "Tuesday
|
||||||
|
/// 6 October at 15:00". Anything else is left as it is.
|
||||||
|
pub fn spoken_datetime(value: &str, norwegian: bool) -> Option<String> {
|
||||||
|
use chrono::{Datelike, NaiveDateTime, Timelike};
|
||||||
|
let t = NaiveDateTime::parse_from_str(value, "%Y-%m-%dT%H:%M").ok()?;
|
||||||
|
let day = t.weekday().num_days_from_monday() as usize;
|
||||||
|
let month = t.month0() as usize;
|
||||||
|
Some(if norwegian {
|
||||||
|
const DAYS: [&str; 7] = ["mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag", "søndag"];
|
||||||
|
const MONTHS: [&str; 12] = ["januar", "februar", "mars", "april", "mai", "juni", "juli", "august", "september", "oktober", "november", "desember"];
|
||||||
|
format!("{} {}. {} kl. {:02}:{:02}", DAYS[day], t.day(), MONTHS[month], t.hour(), t.minute())
|
||||||
|
} else {
|
||||||
|
const DAYS: [&str; 7] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||||
|
const MONTHS: [&str; 12] = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||||
|
format!("{} {} {} at {:02}:{:02}", DAYS[day], t.day(), MONTHS[month], t.hour(), t.minute())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn on_invite(state: &crate::server::AppState, invited: &crate::people::Invited) {
|
pub async fn on_invite(state: &crate::server::AppState, invited: &crate::people::Invited) {
|
||||||
let site = state.site.load();
|
let site = state.site.load();
|
||||||
let Some(cfg) = &site.mail else {
|
let Some(cfg) = &site.mail else {
|
||||||
@@ -560,7 +587,7 @@ pub async fn on_invite(state: &crate::server::AppState, invited: &crate::people:
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let norwegian = site.lang.as_deref().is_some_and(|l| l.starts_with("no") || l.starts_with("nb") || l.starts_with("nn"));
|
let norwegian = site.lang.as_deref().is_some_and(|l| l.starts_with("no") || l.starts_with("nb") || l.starts_with("nn"));
|
||||||
let (subject, body) = match (&cfg.invite, invited.reset_link.is_some(), norwegian) {
|
let (subject, body) = match (invited.mail.as_ref().or(cfg.invite.as_ref()), invited.reset_link.is_some(), norwegian) {
|
||||||
(Some(t), _, _) => (t.subject.clone(), t.body.clone()),
|
(Some(t), _, _) => (t.subject.clone(), t.body.clone()),
|
||||||
(None, true, true) => (
|
(None, true, true) => (
|
||||||
"Du er invitert inn i {group} på {site_name}".to_string(),
|
"Du er invitert inn i {group} på {site_name}".to_string(),
|
||||||
@@ -662,6 +689,13 @@ mod tests {
|
|||||||
assert_eq!(vars["state"], "fixed");
|
assert_eq!(vars["state"], "fixed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_date_and_time_from_a_form_reads_as_a_person_says_it() {
|
||||||
|
assert_eq!(spoken_datetime("2026-10-06T15:00", true).as_deref(), Some("tirsdag 6. oktober kl. 15:00"));
|
||||||
|
assert_eq!(spoken_datetime("2026-10-06T15:00", false).as_deref(), Some("Tuesday 6 October at 15:00"));
|
||||||
|
assert_eq!(spoken_datetime("Anders Kluge", true), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_html_alternative_keeps_markdown_and_drops_raw_html() {
|
fn the_html_alternative_keeps_markdown_and_drops_raw_html() {
|
||||||
let html = to_html("Hello **there**\n\n<script>x()</script>\n\n[link](https://x.no)");
|
let html = to_html("Hello **there**\n\n<script>x()</script>\n\n[link](https://x.no)");
|
||||||
|
|||||||
@@ -254,6 +254,8 @@ pub async fn grant(state: &crate::server::AppState, group: &str, name: &str, who
|
|||||||
reset_link: None,
|
reset_link: None,
|
||||||
link_expires_ms: None,
|
link_expires_ms: None,
|
||||||
created,
|
created,
|
||||||
|
mail: None,
|
||||||
|
fields: Default::default(),
|
||||||
already_member: !created,
|
already_member: !created,
|
||||||
};
|
};
|
||||||
match existing {
|
match existing {
|
||||||
|
|||||||
@@ -127,6 +127,12 @@ pub struct Invited {
|
|||||||
/// the membership is not portal's to take back.
|
/// the membership is not portal's to take back.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub already_member: bool,
|
pub already_member: bool,
|
||||||
|
/// The invite's own mail and the form it was sent from, kept with
|
||||||
|
/// the invite so that sending it again sends the same words.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mail: Option<crate::content::MailTemplate>,
|
||||||
|
#[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
|
||||||
|
pub fields: serde_json::Map<String, serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When a link runs out, from Kanidm's `expiry_time`: seconds since
|
/// When a link runs out, from Kanidm's `expiry_time`: seconds since
|
||||||
@@ -367,6 +373,8 @@ impl Kanidm {
|
|||||||
reset_link,
|
reset_link,
|
||||||
link_expires_ms,
|
link_expires_ms,
|
||||||
created,
|
created,
|
||||||
|
mail: None,
|
||||||
|
fields: Default::default(),
|
||||||
already_member,
|
already_member,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,6 +410,8 @@ mod tests {
|
|||||||
reset_link: None,
|
reset_link: None,
|
||||||
link_expires_ms: None,
|
link_expires_ms: None,
|
||||||
created: true,
|
created: true,
|
||||||
|
mail: None,
|
||||||
|
fields: Default::default(),
|
||||||
already_member,
|
already_member,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+44
-10
@@ -664,11 +664,14 @@ impl Plan {
|
|||||||
// small application and the firm's large one, and is one
|
// small application and the firm's large one, and is one
|
||||||
// page either way. A bucket only a role ever writes to has
|
// page either way. A bucket only a role ever writes to has
|
||||||
// its group's desk and nothing to publish.
|
// its group's desk and nothing to publish.
|
||||||
// Unless it presents: the people are entered from inside
|
// Unless it presents - the people are entered from inside
|
||||||
// precisely so that everyone can see them.
|
// precisely so that everyone can see them - or publishes:
|
||||||
|
// a board's list whose public view is only the stages it
|
||||||
|
// has published (`public_states`) is announcements.
|
||||||
if need.watched_by.iter().any(|g| g == PUBLIC)
|
if need.watched_by.iter().any(|g| g == PUBLIC)
|
||||||
&& need.enters_as.is_some()
|
&& need.enters_as.is_some()
|
||||||
&& !need.presents
|
&& !need.presents
|
||||||
|
&& need.public_states.is_empty()
|
||||||
&& !self
|
&& !self
|
||||||
.needs
|
.needs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1950,13 +1953,12 @@ fn form_alternative(plan: &Plan, need: &PlanNeed, w: &Value, action: &str) -> Va
|
|||||||
if f.multiple { r["multiple"] = json!(true); }
|
if f.multiple { r["multiple"] = json!(true); }
|
||||||
let hint = s(&w["hints"][&f.name]).trim();
|
let hint = s(&w["hints"][&f.name]).trim();
|
||||||
if !hint.is_empty() && f.kind != "select" { r["placeholder"] = json!(hint); }
|
if !hint.is_empty() && f.kind != "select" { r["placeholder"] = json!(hint); }
|
||||||
// Someone signed in is not asked for what the site knows: on a
|
// Someone signed in is not asked for what the site knows, on any
|
||||||
// group's page they always are, and answering a record they
|
// form: portal carries their name and address and the fields take
|
||||||
// may be.
|
// no room (`content::fill_from_user`). A visitor sees the fields
|
||||||
if parent.is_some() || need.enters_as.is_some() {
|
// as usual, so a public form loses nothing by it.
|
||||||
if f.kind == "email" && matches!(f.name.as_str(), "email" | "epost" | "e-post") { r["value"] = json!("{user.email}"); }
|
if f.kind == "email" && matches!(f.name.as_str(), "email" | "epost" | "e-post") { r["value"] = json!("{user.email}"); }
|
||||||
if f.kind == "text" && matches!(f.name.as_str(), "name" | "navn") { r["value"] = json!("{user.name}"); }
|
if f.kind == "text" && matches!(f.name.as_str(), "name" | "navn") { r["value"] = json!("{user.name}"); }
|
||||||
}
|
|
||||||
r
|
r
|
||||||
}).collect();
|
}).collect();
|
||||||
// The record this answer is about, carried in from the page's own
|
// The record this answer is about, carried in from the page's own
|
||||||
@@ -2524,6 +2526,8 @@ pub fn render(plan: &Plan, words: &BTreeMap<String, Value>) -> anyhow::Result<BT
|
|||||||
files.into_iter().map(|(path, value)| Ok((path, serde_yaml::to_string(&value)?))).collect()
|
files.into_iter().map(|(path, value)| Ok((path, serde_yaml::to_string(&value)?))).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAILS_PURPOSE: &str = "the mails the site sends. Under each bucket, each stage that sends one has a `subject` and a `body`: the mail a person gets the moment their case enters that stage, saying what happened and what, if anything, they can do now. Plain text, at most three short sentences plus the link line, no greeting longer than one word, in the business's voice. Keep every `{...}` placeholder exactly as it is - `{site}`, `{chain}`, `{email}` and any field name are filled in by the site - and keep any line with a link in it. The person reads the mail alone, days later, maybe among others, so what works is a mail that says what it is about in their terms - a field they filled in, as a `{placeholder}`, names their case far better than `this` or `your message` (each bucket's fields are listed under the needs above) - and ends with the business's name so they know who wrote. Short fields (a name, a place, a choice) make good subjects; a field marked `(long)` is a whole answer and a field marked `(optional)` may be blank, so those serve better elsewhere or not at all. A desk's decision carries no written reason, but a reply to the mail reaches the people who decided, so inviting a reply is the honest way to offer an explanation.";
|
||||||
|
|
||||||
const DESK_PURPOSE: &str = "a desk: the page one role inside the business works from. Under `buckets`, each entry is a list of cases; `moves` are the buttons that move a case from one stage to the next (key `from->to`), so label each with what the person has just done. Under `links` are pages this role can open. Under `invites`, each entry is a form that invites a person into that group - they get an account and a one-time link - and `invited` is the list of invites sent, where `again` is the button that sends an invite whose link ran out once more. `suggestions`, on the desk that has it, is the list of changes the people responsible for a page have suggested: `looking`, `changed` and `declined` are its buttons.";
|
const DESK_PURPOSE: &str = "a desk: the page one role inside the business works from. Under `buckets`, each entry is a list of cases; `moves` are the buttons that move a case from one stage to the next (key `from->to`), so label each with what the person has just done. Under `links` are pages this role can open. Under `invites`, each entry is a form that invites a person into that group - they get an account and a one-time link - and `invited` is the list of invites sent, where `again` is the button that sends an invite whose link ran out once more. `suggestions`, on the desk that has it, is the list of changes the people responsible for a page have suggested: `looking`, `changed` and `declined` are its buttons.";
|
||||||
|
|
||||||
/// The page about who the business is: a question with nothing to do
|
/// The page about who the business is: a question with nothing to do
|
||||||
@@ -2634,13 +2638,35 @@ fn words_prompt(plan: &Plan, path: &str, fallback: &Value, tone: Option<&str>) -
|
|||||||
Some(n) => format!("a page shown right after someone in the group `{}` has sent a form from inside the business. The reader is that colleague, at work, not a visitor: nobody comes back to them. Say what has now happened to what they entered, and who it reaches.", n.enters_as.as_deref().unwrap_or_default()),
|
Some(n) => format!("a page shown right after someone in the group `{}` has sent a form from inside the business. The reader is that colleague, at work, not a visitor: nobody comes back to them. Say what has now happened to what they entered, and who it reaches.", n.enters_as.as_deref().unwrap_or_default()),
|
||||||
None => "a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.".to_string(),
|
None => "a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.".to_string(),
|
||||||
};
|
};
|
||||||
|
// Who reads each mail. A mail to the business about someone else's
|
||||||
|
// case was written as if to the person who sent it ("you will hear
|
||||||
|
// from us") until the model was told who opens it.
|
||||||
|
let recipients: Vec<String> = plan
|
||||||
|
.needs
|
||||||
|
.iter()
|
||||||
|
.flat_map(|n| {
|
||||||
|
n.mails.iter().map(move |(stage, to)| {
|
||||||
|
let reader = match to.as_str() {
|
||||||
|
"submitter" => "the person who sent it".to_string(),
|
||||||
|
"owner" => "the person who posted the record this answers".to_string(),
|
||||||
|
address => format!("someone at the business ({address}), about another person's case; write to them, not to the person who sent it"),
|
||||||
|
};
|
||||||
|
format!("- `{}` entering `{stage}`: read by {reader}", n.lands_in)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mails_purpose = format!(
|
||||||
|
"{MAILS_PURPOSE}\n\nWho opens each mail:\n{}",
|
||||||
|
recipients.join("\n")
|
||||||
|
);
|
||||||
let who_purpose = "the page about who the business is, with nothing to do on it, only things to read. `question` asks it as a question (\"Who are we?\"). Under `presents`, each entry heads one list shown as cards with a picture each - the people who work there, the work they have done - loosely, as a person would introduce them. `values` heads the business's own statements, which are its facts: head them, never add to them.".to_string();
|
let who_purpose = "the page about who the business is, with nothing to do on it, only things to read. `question` asks it as a question (\"Who are we?\"). Under `presents`, each entry heads one list shown as cards with a picture each - the people who work there, the work they have done - loosely, as a person would introduce them. `values` heads the business's own statements, which are its facts: head them, never add to them.".to_string();
|
||||||
let purpose = if path == who_page_path(plan) {
|
let purpose = if path == who_page_path(plan) {
|
||||||
who_purpose.as_str()
|
who_purpose.as_str()
|
||||||
} else if path == "site.yaml" {
|
} else if path == "site.yaml" {
|
||||||
"the invite mail: the first mail a person gets when a member of the business brings them in to help (onto a desk, into a committee), carrying the one-time link that lets them set a password. `subject` and `body` under `invite`. They did not ask for it, so say who brought them in and to what, then the link and when it runs out, then where they sign in afterwards. Plain text, short lines. Keep every `{...}` placeholder exactly as it is: `{name}`, `{username}`, `{group}`, `{link}`, `{expires}`, `{site}`, `{site_name}` are filled in by the site, and `{link}` must stay on a line of its own."
|
"the invite mail: the first mail a person gets when a member of the business brings them in to help (onto a desk, into a committee), carrying the one-time link that lets them set a password. `subject` and `body` under `invite`. They did not ask for it, so say who brought them in and to what, then the link and when it runs out, then where they sign in afterwards. Plain text, short lines. Keep every `{...}` placeholder exactly as it is: `{name}`, `{username}`, `{group}`, `{link}`, `{expires}`, `{site}`, `{site_name}` are filled in by the site, and `{link}` must stay on a line of its own."
|
||||||
} else if path == "aggregates.yaml" {
|
} else if path == "aggregates.yaml" {
|
||||||
"the mails the site sends. Under each bucket, each stage that sends one has a `subject` and a `body`: the mail a person gets the moment their case enters that stage, saying what happened and what, if anything, they can do now. Plain text, at most three short sentences plus the link line, no greeting longer than one word, in the business's voice. Keep every `{...}` placeholder exactly as it is - `{site}`, `{chain}`, `{email}` and any field name are filled in by the site - and keep any line with a link in it. The person reads the mail alone, days later, maybe among others, so what works is a mail that says what it is about in their terms - a field they filled in, as a `{placeholder}`, names their case far better than `this` or `your message` (each bucket's fields are listed under the needs above) - and ends with the business's name so they know who wrote. Short fields (a name, a place, a choice) make good subjects; a field marked `(long)` is a whole answer and a field marked `(optional)` may be blank, so those serve better elsewhere or not at all. A desk's decision carries no written reason, but a reply to the mail reaches the people who decided, so inviting a reply is the honest way to offer an explanation."
|
mails_purpose.as_str()
|
||||||
|
|
||||||
} else if path.contains("/decide/") {
|
} else if path.contains("/decide/") {
|
||||||
"a page the person who sent a form reaches later, through a private link, to make a decision that is theirs: under `moves`, each entry is one thing they can decide."
|
"a page the person who sent a form reaches later, through a private link, to make a decision that is theirs: under `moves`, each entry is one thing they can decide."
|
||||||
} else if path == "questions/index.yaml" {
|
} else if path == "questions/index.yaml" {
|
||||||
@@ -3223,6 +3249,14 @@ mod tests {
|
|||||||
assert!(bad.validate().unwrap_err().to_string().contains("multiple is for select"));
|
assert!(bad.validate().unwrap_err().to_string().contains("multiple is for select"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_words_model_is_told_who_opens_each_mail() {
|
||||||
|
let plan: Plan = serde_yaml::from_str("business: Vel\ncontact: { name: S, email: s@v.example }\nmail: { from: v@v.example }\nneeds:\n - id: notice\n who: en nabo\n wants: et oppslag\n lands_in: oppslag\n handled_by: redaktor\n stages: [open, tatt_ned]\n done_when: [tatt_ned]\n mails: { open: s@v.example }\n watched_by: [public]\n max_fields: 2\n collects:\n - { name: email, type: email }\n - id: answer\n who: en nabo\n wants: svare\n about: notice\n lands_in: svar\n handled_by: redaktor\n stages: [open, lest]\n done_when: [lest]\n mails: { open: owner }\n max_fields: 1\n collects:\n - { name: email, type: email }\n").unwrap();
|
||||||
|
let prompt = words_prompt(&plan, "aggregates.yaml", &json!({}), None);
|
||||||
|
assert!(prompt.contains("`oppslag` entering `open`: read by someone at the business (s@v.example)"), "{prompt}");
|
||||||
|
assert!(prompt.contains("`svar` entering `open`: read by the person who posted the record this answers"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_need_no_persona_tries_is_named() {
|
fn a_need_no_persona_tries_is_named() {
|
||||||
let plan: Plan = serde_yaml::from_str("business: B\ncontact: { name: I, email: i@b.example }\nmail: { from: post@b.example }\nneeds:\n - id: ask\n who: a customer\n wants: an answer\n lands_in: asks\n handled_by: desk\n stages: [open, done]\n done_when: [done]\n max_fields: 2\n collects:\n - { name: email, type: email }\n - id: card\n who: one of us\n wants: to be seen\n enters_as: desk\n lands_in: cards\n handled_by: desk\n stages: [open, gone]\n done_when: [gone]\n max_fields: 1\n collects:\n - { name: name }\n - id: later\n who: someone\n wants: something\n paused: true\n lands_in: later\n handled_by: desk\n stages: [open, done]\n done_when: [done]\n max_fields: 1\n collects:\n - { name: email, type: email }\npersonas:\n - { id: p, need: ask, text: I need an answer }\n").unwrap();
|
let plan: Plan = serde_yaml::from_str("business: B\ncontact: { name: I, email: i@b.example }\nmail: { from: post@b.example }\nneeds:\n - id: ask\n who: a customer\n wants: an answer\n lands_in: asks\n handled_by: desk\n stages: [open, done]\n done_when: [done]\n max_fields: 2\n collects:\n - { name: email, type: email }\n - id: card\n who: one of us\n wants: to be seen\n enters_as: desk\n lands_in: cards\n handled_by: desk\n stages: [open, gone]\n done_when: [gone]\n max_fields: 1\n collects:\n - { name: name }\n - id: later\n who: someone\n wants: something\n paused: true\n lands_in: later\n handled_by: desk\n stages: [open, done]\n done_when: [done]\n max_fields: 1\n collects:\n - { name: email, type: email }\npersonas:\n - { id: p, need: ask, text: I need an answer }\n").unwrap();
|
||||||
|
|||||||
+67
-5
@@ -162,10 +162,13 @@ main.not-found {
|
|||||||
|
|
||||||
/* hero */
|
/* hero */
|
||||||
|
|
||||||
|
/* No room is kept at the top for who is here: that sits on the
|
||||||
|
wordmark's own line now (.hero-top), and the 4rem it used to need
|
||||||
|
read as dead space above the mark on a tall, narrow screen. */
|
||||||
.hero {
|
.hero {
|
||||||
max-width: 46rem;
|
max-width: 46rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 4rem 1.5rem 2.5rem;
|
padding: 1.75rem 1.5rem 2.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -176,10 +179,6 @@ main.not-found {
|
|||||||
/* Who is here and the way in or out: top right of every page, on a
|
/* Who is here and the way in or out: top right of every page, on a
|
||||||
site that has sign-in. */
|
site that has sign-in. */
|
||||||
.session {
|
.session {
|
||||||
position: absolute;
|
|
||||||
top: 1rem;
|
|
||||||
right: 1.5rem;
|
|
||||||
z-index: 5;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.9rem;
|
gap: 0.9rem;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
@@ -201,6 +200,55 @@ main.not-found {
|
|||||||
color: var(--ink-dim);
|
color: var(--ink-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The wordmark's line, with who is here on it: the mark centred and the
|
||||||
|
session at the right edge, level with the mark. A skin that sets
|
||||||
|
everything on one left edge spreads the two apart instead (see
|
||||||
|
Klingenberg Bygg's and Gullberg's). On a narrow screen the session
|
||||||
|
drops under the mark rather than over it. */
|
||||||
|
.hero-top {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-top .session {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-bar .hero-top {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-bar .hero-top .session {
|
||||||
|
position: static;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 34rem) {
|
||||||
|
.hero {
|
||||||
|
padding-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-bar {
|
||||||
|
padding-block: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-top {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-top .session {
|
||||||
|
position: static;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* A page with pictures: the wordmark is a bar of its own, the strip
|
/* A page with pictures: the wordmark is a bar of its own, the strip
|
||||||
runs the full width under it, and the question follows. */
|
runs the full width under it, and the question follows. */
|
||||||
.hero-slides {
|
.hero-slides {
|
||||||
@@ -244,6 +292,20 @@ main.not-found {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A wordmark in the page's ink (site.yaml `wordmark_ink`): the file is a
|
||||||
|
mask over the text colour. The invisible image inside gives the span
|
||||||
|
the file's own size. */
|
||||||
|
.wordmark-ink-mark {
|
||||||
|
display: block;
|
||||||
|
background: var(--ink);
|
||||||
|
-webkit-mask: var(--wordmark-src) center / contain no-repeat;
|
||||||
|
mask: var(--wordmark-src) center / contain no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordmark-ink-mark img {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-copy h1 {
|
.hero-copy h1 {
|
||||||
font-size: 2.1rem;
|
font-size: 2.1rem;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user